Pull to refresh

Редактируемая таблица — CMS своими руками

Level of difficultyMedium

Построение таблицы данных GRID по JSON файлу с возможностью редактирования

Настало время рассмотреть пример приближенный к реальной задаче. Допустим, есть на сервере файл в JSON формате, который нужно форматировать через интерфейс. Для этого может пригодиться компонент в виде редактируемой таблицы GRID.

Итак, создадим компонент GRID для редактирования текстовых файлов в формате JSON

В этом компоненте бизнес- логика будет на PHP, ядровая часть на PHP, JQUERY, HTML, CSS

Проект состоит из файлов:

file.json - исходный файл с данными
file_grid.js - Java Script файл с вспомогательными функциями
file_grid.php - файл с бизнес логикой
lib.php - файл с библиотекой
utils.php - файл с общими вспомогательными функциями
jquery-3.7.1.min.js - файл библиотеки Jquery, который можно взять с jquery.com

Все файлы лежат в одной папке. Точкой входа в проект является файл file_grid.php.

При запуске отображается таблица, которую можно редактировать.

Исходное отображение таблицы
Исходное отображение таблицы

Любое значение таблицы можно поменять и сохранить в исходный файл. Войти в редактирование - двойной щелчок мыши, отменить изменение Ctrl+Z, запостить - сместить мышкой фокус на другую ячейку или область экрана. Сохранит данные на сервере - нажатие кнопки “Сохранить”.

Отображение inline редактора
Отображение inline редактора

При нажатии на кнопку “Сохранить” вызывается событие в PHP файле, в котором происходит сохранение и выдается сообщение “Сохранено!”.

Сообщение о сохранении данных на сервере
Сообщение о сохранении данных на сервере

Данные в исходном файле file.json:

[
  {
	"name": "Samsung",
	"price": "51201.62"
  },
  {
	"name": "Lg",
	"price": "5400.6"
  },
  {
	"name": "Alcatel",
	"price": "4503.6"
  }
]

Формат исходных данных представляет из себя набор записей с одинаковым количеством столбцов в каждой из них. 

Состав файла file_grid.js:

<?php

$(document) . ready(
	function() {
		$("body") . delegate(
			'[change_cell]',
			"change",
			function(event) {
				id = $(this) . attr('id');
				parentid = $(this) . attr('parentid');
				val = event . target . value;
				v = $('JSN') . text();
				jsn = JSON . parse(v);
				if (jsn) {
					jsn[parentid][id] = val;
				};
				s = JSON . stringify(jsn);
				$('JSN') . text(s);
			}
		);

		function getparams($attr)
		{
			req = '[';
			if ($attr) {
				jsn = JSON . parse($attr);
				var name;
				var param;
				var attrname;
				var value;
				for (var i = 0; i < jsn . length; i++) {
					name = jsn[i] . name;
					id = jsn[i] . id;
					param = jsn[i] . param;
					attrname = jsn[i] . attrname;
					var type = jsn[i] . type;
					if (type === "value") {
						if (name) value = $('[name='+name + ']') . val();
						if (id) value = $('[id='+id + ']') . val();
					}
					if (type === "attr") {
						if (name) value = $('[name='+name + ']') . attr(attrname);
						if (id) value = $('[id='+id + ']') . attr(attrname);
					}
					if (type === "text") {
						if (name) value = $('[name='+name + ']') . text();
						if (id) value = $('[id='+id + ']') . text();
					}
					if (type === "html") {
						if (name) value = $('[name='+name + ']') . html();
						if (id) value = $('[id='+id + ']') . html();
					}
					if (req !== '[') req = req + ',';
					req = req + '{"'+param + '":"'+encodeURI(value) + '"}';
				}
			}
			req = req + ']';
			return req;
		}

		$("body") . delegate(
			'[click]',
			"click",
			function() {
				req = getparams($(this) . attr('request'));

				$. post(
					'file_grid.php', {
						type:"event",
						name:$(this
					) . attr('click'), request:req
				},
				onSuccess
			);
		}
	);

	function onSuccess(data)
	{
		jsn = JSON . parse(data);
		if (!jsn . exec == '') {
			eval(jsn . exec);
		}
		if (!jsn . message == '') {
			alert(jsn . message);
		}
	}
});

В java-script файле file_grid.js реализовано делегирование события изменения ячейки change,  при котором записывает значение ячейки в JSON объект HTML формы. JSON объект хранится в теге JSN. Так же реализовано делегирование события click кнопки сохранения таблицы, из него вызывается событие в PHP файле, где и происходит это изменение. Вспомогательная функция getparams формирует JSON для передачи из JS скрипта в PHP событие.

Состав файла file_grid.php:

<?php

include('utils.php');

function event_click($data)
{
	$mes = 'Сохранено!';

	$text = urldecode(getval($data, 'jsn'));
	$file = urldecode(getval($data, 'file'));

	file_put_contents($file, $text);

	$jsn = json_encode(['result' => 'OK', 'message' => $mes]);

	return $jsn;
}

if (isset($_POST['type'])) {
	if ($_POST['type'] == 'event') {
		if (isset($_POST['name'])) {
			$request = array();
			if (isset($_POST['request'])) {
				$request = $_POST['request'];
			}
			echo call_user_func($_POST['name'], $request);
		}
	}
} else {
	include('lib.php');

	echo '<!DOCTYPE html>
	<html lang="en">
	<head>
        	<meta charset="UTF-8">
        	<title>edited grid</title>
        	<script src="jquery-3.7.1.min.js"></script>
        	<script src="file_grid.js"></script>
	</head>
	<body>';

	$e = new file_grid;
	$e->id = 'file_grid';
	$e->file = 'file.json';
	echo $e->print();

	echo '
	</body>
	</html>';
}

Файл file_grid.php служит для первоначальной отрисовки таблицы, а так же для обработки событий, которые вызываются из браузера. event_click - событие сохранения таблицы. call_user_func - стандартная функция PHP для CALLBACK вызова event_click. Переменная $e содержит экземпляр класса, который описан в файле lib.php. $e->print() - вывод на экран таблицы данных.

Состав файла lib.php:

<?php

define(
	'STYLE',
	'<style>
	grid{
  	border-top: 1px solid black;
  	border-left: 1px solid black;
  	display: table;
	}
	row{
  	display: table-row;  
	} 	 
	cell{
  	display: table-cell;
  	border-right: 1px solid black;  
  	border-bottom: 1px solid black;   	 
	}
	hrow{
  	display: table-row;  
	}  	 
	hcell{
  	background-color: grey;
  	color: white;  	 
  	display: table-cell;
  	border-right: 1px solid black;  
  	border-bottom: 1px solid black;   	 
	}  	 
	JSN{
  	display: none;
	}
 </style>'
);
define('CONTENT', '%content%');
define('ID', '%id%');
define('PARENTID', '%parentid%');
define('ELEMENT', '<div id=' . ID . ' >' . CONTENT . '</div>');
define(
	'CELL',
	'<cell name=cell_' . PARENTID . '_' . ID . ' '
		. 'id=' . ID . ' parentid=' . PARENTID . ' '
		. 'change_cell="" '
		. 'ondblclick=" '
		. "elem1 = $('[name=cell_" . PARENTID . "_" . ID . "]'); "
		. 'cont = elem1.text(); '
		. 'elem1.empty(); '
		. 'elem1.append(&quot;<textarea '
		. 'name=textarea_' . PARENTID . '_' . ID . ' '
		. "onfocusout=\&quot; "
		. "elem1 = $('[name=cell_" . PARENTID . "_" . ID . "]'); "
		. "elem2 = $('[name=textarea_" . PARENTID . "_" . ID . "]'); "
		. 'cont = elem2.val(); '
		. 'elem1.empty(); '
		. 'elem2.remove(); '
		. 'elem1.append(cont); '
		. '\&quot;>&quot;+cont+&quot;</textarea>&quot;); '
		. 'elem2 = $(&quot;[name=textarea_' . PARENTID . '_' . ID . ']&quot;); '
		. 'elem2.focus(); '
		. '" '
		. '>' . CONTENT . '</cell>'
);
define('ROW', '<row id=' . ID . ' >' . CONTENT . '</row>');
define('HCELL', '<hcell>' . CONTENT . '</hcell>');
define('HROW', '<hrow id=' . ID . ' >' . CONTENT . '</hrow>');
define('GRID', STYLE . '<grid id=' . ID . ' >' . CONTENT . '</grid>');
define('SAVE_CONTENT', 'Сохранить');
define('JSN', '%jsn%');
define('JSN_NAME', 'JSN_NAME');
define('JSN_CONT', '<JSN name=' . JSN_NAME . '>' . JSN . '</JSN>');
define(
	'BUTTON_SAVE',
	'<button name=button_save click=event_click '
		. 'request=[{"type":"text","name":"' . JSN_NAME . '","param":"jsn"},'
		. '{"type":"attr","attrname":"file","id":"' . ID . '","param":"file"}] '
		. '>' . SAVE_CONTENT . '</button>'
);
define('FILE_NAME', '%file_name%');
define('FILE_GRID', STYLE . '<grid id=' . ID . ' file= ' . FILE_NAME . ' >' . CONTENT . JSN_CONT . BUTTON_SAVE . '</grid>');

class element
{
	public $template;
	public $content;
	public $child;
	public $id;
	public $hasparent;
	public $parentid;
	public function __construct()
	{
		if (!isset($this->template)) {
			$this->template = ELEMENT;
		}
		$this->content = array();
	}
	function getclass()
	{
		if (isset($this->child)) {
			return new $this->child;
		} else {
			return new element;
		}
	}
	public function print($content = '') {
		if (is_array($this->content)) {
   		 //$content = '';
			foreach ($this->content as $k => $value) {
				$e = $this->getclass();
				$e->content = $value;
				$e->id = $k;
				if ($e->hasparent) {
					$e->parentid = $this->id;
				}
				$content .= $e->print();
			}
		} else {
			$content = $content . $this->content;
		}
		$ret = str_replace(
			array(ID, CONTENT, PARENTID),
			array($this->id, $content, $this->parentid),
			$this->template
		);
		return $ret;
	}
}

class cell extends element
{
	public function __construct()
	{
		$this->template = CELL;
		$this->hasparent = true;
		parent::__construct('');
	}
}

class row extends element
{
	public function __construct()
	{
		$this->template = ROW;
		$this->child = 'cell';
		parent::__construct();
	}
}

class hcell extends element
{
	public function __construct()
	{
		$this->template = HCELL;
		$this->hasparent = true;
		parent::__construct('');
	}
}

class hrow extends element
{
	public function __construct()
	{
		$this->template = HROW;
		$this->child = 'hcell';
		parent::__construct();
	}
}

class grid extends element
{
	public $header;
	public function __construct()
	{
		$this->template = GRID;
		$this->child = 'row';
		parent::__construct();
	}
	public function print($content = '') {
		$e = new hrow;
		$e->content = $this->header;
		$content = $e->print();
		return parent::print($content);
	}
}

class file_grid extends grid
{
	public $file;
	public $json;
	public function __construct()
	{
		parent::__construct();
		$this->template = FILE_GRID;
	}
	public function print($content = '') {
        	//$this->header = array('1'=>'column1','2'=>'column2');
		$this->json = file_get_contents($this->file);
		$arr = json_decode($this->json, true);
		foreach ($arr as $key => $value) {
			foreach ($value as $vkey => $vvalue) {
				$this->header[] = $vkey;
			}
			break;
		}
		$this->content = $arr;
		$ret = parent::print('');
		$ret = str_replace(
			array(JSN, FILE_NAME),
			array($this->json, $this->file),
			$ret
		);
		return $ret;
	}
}

В файле lib.php описаны базовые константы через стандартную функцию defile, а так же описана иерархия классов. Базовым классом является ELEMENT. Далее от него наследуется ROW, CELL, GRID. От GRID наследуется FILE_GRID. В каждом классе прописана своя специфическая логика. В константах задана как HTML, CSS разметка, так и события JAVA SCRIPT, такие как DBLCLICK и FOCUSOUT. Классы используют эти константы чтоб отрисовывать в статике и динамике HTML разметку.

Состав файла utils.php:

<?php

function getval($data, $name){    
	$rjsn = json_decode($data, true);
	foreach ($rjsn as $vjsn){
    	foreach ($vjsn as $key => $value) {
        	if($name == $key){
            	return $value;
        	}    
    	}	 
	}	 
	return 'undefined';
}

Функция getval используется для получения значения параметра по имени внутри PHP события.

Таким образом реализована редактируемая табличная форма данных, получаемая из JSON файла.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.