Форма добавления и редактирования сущностей

Ссылки на форму редактирования

В таблицах админки по управлению контентом у нас есть ссылки на форму редактировани: кнопка добавить запись и кнопка редактирования конкретной записи. Они имеют содержат ссылки:

<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>

Обработчик полей файлов

Оставляю в качестве упражнения читателю написать отдельную обработку для значения файловых полей.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *