Ссылки на форму редактирования
В таблицах админки по управлению контентом у нас есть ссылки на форму редактировани: кнопка добавить запись и кнопка редактирования конкретной записи. Они имеют содержат ссылки:
<a href="/admin/form.php?table=product&action=add">+ Товар</a>
<a class="control_link" href="/admin/form.php?table=product&action=edit&id=<?=$element['id'];?>" title="Редактировать"><i class="fa fa-pencil"></i></a>
От сущности к сущности будет меняться значения GET-параметра table. Этот параметр поможет нам в написании единственной формы редактирования записи для всех сущностей.
Выборка структуры таблицы
Структуру формы мы сформируем по данным из системной таблицы information_schema.COLUMNS.
При редактировании, данные для наполнения формы будем выбирать из соответствующей записи таблицы по параметру $table (да тут небезопасный запрос, можете переделать на безопасный).
$schema = [];
$table = $_GET['table'];
$action = $_GET['action'] == 'edit' ? 'edit': 'add';
if (isset($_GET['table']) && $_GET['table']) {
$query = 'SELECT column_name, DATA_TYPE FROM information_schema.COLUMNS WHERE table_name=?;';
$result = $pdo->prepare($query);
// print_r($pdo->errorInfo());
$result->execute([$table]);
$schema = $result->fetchAll();
// для редактирования выберем данные записи для заполнения формы
$formData = [];
if ($_GET['id']) {
$query = 'SELECT * FROM '. $table .' WHERE id=?;';
$result = $pdo->prepare($query);
$result->execute([$_GET['id']]);
$formData = $result->fetch();
}
}
Разметка формы
Базово форму редактирования будем создавать следующим циклом:
<form action="/admin/edit.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="table" value="<?=$table;?>">
<input type="hidden" name="action" value="<?=$action;?>">
<?foreach ($schema as $key => $element) {
if ($element['column_name'] == 'id') {?>
<input type="hidden" name="<?=$element['column_name'];?>" value="<?=$_GET['id'];?>">
<?} else {?>
<p>
<label><?=$element['column_name']?></label>
<input type="text" name="<?=$element['column_name'];?>" value="<?=$formData[$element['column_name']]?>">
</p>
<?}?>
<?}?>
<button type="submit" class="btn btn-default">Сохранить</button>
</form>
Но сейчас у нас все поля сформируются текстовыми. Без учёта таких полей как файлы картинок, перечисляемые enum или поля внешних ключей. Чуть позже мы это поправим.
Обработчик формы
<?require_once('./setting.php');
// обрабатываем данные из формы
$error = [];
$schema_edit = [];
// нет данных. Ничего не делаем просто подключаем форму
if (empty($_POST) || !$_POST['table']) {
} else {
$table = 'wshop_' . $_POST['table'];
$id = (int) $_POST['id'];
$query = 'SELECT column_name, DATA_TYPE FROM information_schema.COLUMNS WHERE table_name=?;';
$result = $pdo->prepare($query);
$result->execute([$table]);
$schema_edit = $result->fetchAll();
// цель - сформировать запрос вставки или обновления
$data = $_POST;
unset($data['table']);
unset($data['action']);
unset($data['id']);
$fields = [];
$updateData = [];
foreach ($data as $key => $value) {
$fields[] = $key;
$updateData[] = '`'. $key .'`=:'. $key;
}
if (!empty($fields)) {
$field_string = '`' . implode('`, `', $fields) . '`';
$value_string = ':' . implode(', :', $fields) . '';
$update_string = implode(', ', $updateData);
// есть id - update
$query = $id ?
'UPDATE '. $table .'
SET '. $update_string .' WHERE id=' . $id:
'INSERT INTO '. $table .' ('. $field_string .')
VALUES ('. $value_string .')';
$stmt = $pdo->prepare($query);
$stmt->execute($data);
header('Location: /admin/crud_'. $_POST['table'] .'.php');
die;
} else {
$error[] = 'нет полей для записи';
}
}
// echo '<pre>';
// print_r($query);
// print_r($data);
// echo '</pre>';
require('./form.php');
?>
Модели для особых полей таблиц
Теперь давайте создадим маленькие псевдомодели, которые помогут нам выводить особые поля таблиц по особенному:
// модель: удалённые, скрытые поля, файлы, селекты: связи и перечислений.
$query = 'SELECT * FROM `wshop_category` WHERE `type` = "catalog";';
$result = $pdo->query($query);
$categorySelect = $result->fetchAll();
$query = 'SELECT * FROM `wshop_role` WHERE 1;';
$result = $pdo->query($query);
$roleSelect = $result->fetchAll();
$result = str_replace(array("enum('", "')", "''"), array('', '', "'"), $schema[3]['COLUMN_TYPE']);
$menuTypeSelect = explode("','", $result);
$models = [
'product' => [
'image' => ['file', 'jpg,png,jpeg'],
'category_id' => ['select', $categorySelect],
],
'category' => [
'image' => ['file', 'jpg,png,jpeg'],
],
'user' => [
'role_id' => ['select', $roleSelect],
],
'menu_item' => [
'type' => ['enum', $menuTypeSelect],
],
];
Здесь все селекты создаются всегда – независимо от того с какой таблицей мы работаем. Вы может оптимизировать этот код если будете создавать селекты только для той таблицы с которой работаете в данный момент.
Так же вы можете добавить в модели понятие удалённых полей – например, чтобоы не выводить хэш пароля пользователя. Скрытые поля – чтобы например не выделять id как особое поле, а просто в модели указывать:
'id' => ['hide', 0],
Вывод особых полей
Там, где мы циклом выводим поля, между конструкциями if и else нужно вставить код для особых полей модели:
<?} elseif($models[$table][$element['column_name']][0] == 'file') {?>
<label><?=$element['column_name']?></label>
<input type="file" name="<?=$element['column_name'];?>" value="<?=$formData[$element['column_name']]?>">
<?} elseif($models[$table][$element['column_name']][0] == 'select') {
// выбрать значения для селекта?>
<label><?=$element['column_name']?></label>
<select>
<?foreach($models[$table][$element['column_name']][1] as $option) {
$selected = $option['id'] == $formData[$element['column_name']] ? 'selected="selected"': '';?>
<option value="<?=$option['id'];?>" <?=$selected;?>><?=$option['name'];?></option>
<?}?>
</select>
<?} elseif($models[$table][$element['column_name']][0] == 'enum') {
// выбрать значения для селекта?>
<label><?=$element['column_name']?></label>
<select>
<?foreach($models[$table][$element['column_name']][1] as $option) {
$selected = $option == $formData[$element['column_name']] ? 'selected="selected"': '';?>
<option value="<?=$option;?>" <?=$selected;?>><?=$option;?></option>
<?}?>
</select>
Обработчик полей файлов
Оставляю в качестве упражнения читателю написать отдельную обработку для значения файловых полей.