Необычная работа с формами на PHP или как упростить себе жизнь

Всем привет! Хочу показать как можно работать с формами не прибегая к «фримворкам» в несколько килобайт кода.

Вступление

Как то раз пришлось мне создать небольшой проект портала с большим количеством форм как в «back» так и в «front» и тут возник вопрос что использовать? Сначала решил прикрутить что то на подобии Symfony. Но при разработке вышло такое количество строк кода что через некоторое время я сам начал путаться. И тут я решил изобрести велосипед.

Разработка

Для начала создал все таблицы в БД, вот например листинг одной:

CREATE TABLE IF NOT EXISTS `cms_articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `article` text,
  `author` varchar(255) DEFAULT NULL,
  `edited` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `url` varchar(100) DEFAULT NULL,
  `rating` int(10) NOT NULL DEFAULT '0',
  `views` int(10) NOT NULL DEFAULT '0',
  `comments` int(10) unsigned NOT NULL DEFAULT '0',
  `parent` int(10) NOT NULL DEFAULT '0',
  `key` text NOT NULL,
  `desc` text NOT NULL,
  `tags` varchar(250) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Следующим этапом стало написание HTML кода для формы. 
И тут начинается суть моего способа: название элементов в шаблоне должно соответствовать названиям полей таблицы.

<body>
<font size='5px' class='title'>[page:name]</font>
<span style="float: right; padding-top: 10px;">[page:path]</span>

[page:message]

<form class="form-list" action="" method="post" name="Form" enctype="multipart/form-data" accept-charset="UTF-8">

<div class="section">
<ul class="tabs">
    <li class="current"><a href="#tab1">Основные параметри статьи</a></li>
    <li class=""><a href="#tab2">Текст статьи</a></li>
</ul>

<div class="boxu visible">

<div class="item">
    <label>Заголовок</label><span class="required">*</span><br>
        <input type="text" class="big" value="" name="title">
    <br>
    <label class="descript">Заголовок должен содержать смысл страницы</label>    
</div>

<div class="item">
    <label>Родитель</label><br>                       	
    <select class="big" name="parent" id="">
        <option selected value="153">- без родителя -</option>
        [page:parent]
    </select><br>
    <label class="descript">Содержимое, к которому добавляется страница</label></div><div class="item ">

    <label>Адрес</label><br>
    <input type="text" class="medium" value="" name="url">
    <br>
    <label class="descript">URL адрес страницы</label>    
</div>

<div class="item">
    <label>Автор</label><br>
        <input type="text" class="big" value="" name="author">
    <br>
    <label class="descript">Автор статти ...</label>    
</div>

<div class="item">
    <label>Описание</label><br>                       	
    <textarea  class="" name="desc" rows="2"></textarea><br>
  	<label class="descript">Краткое описание сайта. Используются поисковыми системами (Яндекс, Google...)</label>
</div>

<div class="item">
    <label>Ключевые слова</label><br>                       	
    <textarea name="key" rows="2"></textarea><br> 
    <label class="descript">Ключевые слова через запятую. Используются поисковыми системами (Яндекс, Google...)</label>
</div>

<table width="600px">
<tr><td>

<div class="item">
    <label>Рейтинг</label><br>
        <input type="text" class="lbig" value="" name="rating">
    <br>
    <label class="descript">Рейтинг статти ...</label>    
</div>

</td><td>

<div class="item">
    <label>Просмотры</label><br>
        <input type="text" class="lbig" value="" name="views">
    <br>
    <label class="descript">Количество просмотров ...</label>    
</div>

</td></tr>
</table>

<div class="item">
    <label>Разрешыть коментирование</label><br>  
    <span><input name="comments" type="radio" value="1" checked>Да</span>
    <span><input name="comments" type="radio" value="0">Нет</span><br>
    Пользователи могут оставлять коментарии.<br>
</div>

</div>

<div class="boxu">

<div class="item">
    <label>Текст</label><br>                       	
    <textarea name="article" rows="28" id="article"></textarea><br>
  	<label class="descript">Текст страницы. Разрешено использование HTML тегов</label>
</div>

<div class="item">
    <label>Теги</label><br>
        <input type="text" style="width: 200px;" value="" name="tags" id="tags"><br>
    <label class="descript">Теги к даному тексту (Вводить через кому).</label>    
</div>

<div class="item-buttons">
    <input type="submit" name="submit" id="submit" value="Сохранить">
</div>

</div>
</div>		
</form>
</body>

Вот как выглядит форма (Орфографические ошибки на рисунке остались от заказчика я их не правил):
759b1fb19b9edbdc17019f5d0ef47d13

Далее сохранил шаблон в файл, под названием «article.tpl». И начал писать обработчик. Для его написание решил использовать DOM кто хочет пускай перепишет под «simpleDom».

Вот маленькая функция которая находит элемент по названию а не по ID.

    private function GetElementByName($_dom, $_name){
        // Experimental search by name
        $xpath = new DOMXPath($_dom);
        return $xpath->query('//*[@name="'.$_name.'"]')->item(0);
    }

Вот тут я написал функцию которая автоматически заполняет форму из таблицы. Учитывая тип элемента и его свойства.

    public function LoadShowFormAll($query, $template) {

    $page_encoding = 'UTF-8';

    $dom = new DomDocument();
    @$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=' . $page_encoding . '">' .  file_get_contents(TMP_DIR.$template.".tpl"));

    if ($result = $this->base->select($query)) {

    foreach ($result as $key => $value) {	
        $obj = $this->GetElementByName($dom, $value['name']);  //$dom->getElementById($val->name);

            switch ($obj->nodeName) {
                case "input":
                    if ($obj->getAttribute('type') == 'radio') {

                        $optionTags = $dom->getElementsByTagName('input');
                        for ($i = 0; $i < $optionTags->length; $i++) {

                            if ($optionTags->item($i)->getAttribute('name') == $value['name']) {
                                if ($optionTags->item($i)->getAttribute('value') == $value['param']) {

                                    $optionTags->item($i)->SetAttribute('checked', 'checked');
                                }
                            }
                        }

                        if ($value['param'] == 1) {
                            $obj->SetAttribute('checked', 'checked');
                        }
                    } else
                    if ($obj->getAttribute('type') == 'checkbox') {

                        if ($value['param'] == 1) {
                            $obj->SetAttribute('checked', 'checked');
                        }
                    } else {
                        $obj->SetAttribute('value', $value['param']);
                    }
                    break;

                case "textarea":
                    $obj->nodeValue = $value['param'];
                    break;

                case "select":
                    $optionTags = $obj->getElementsByTagName('option');
                    for ($i = 0; $i < $optionTags->length; $i++) {

                        if ($optionTags->item($i)->getAttribute('value') == $value['param']) {
                            $optionTags->item($i)->SetAttribute('selected', 'selected');
                        }
                    }
                    break;

                case "":
                    break;
            }
        }
    }	
    preg_match_all("#<body>(.*)</body>#isUu", $dom->saveHTML(), $str);	
    return $str[1][0];
    }

Использовать следующим образом:

echo $Core->LoadShowForm('SELECT * FROM cms_articles WHERE id='.(int)($get_id), 'article');

После этого возникла необходимость сохранять значения в базу и изменять их для этих заданий я написал две простые функции.

    public function InsertNewValue($post, $table, $custom = null) {

    $query = '';
    $sep = false;
    $tmp = array();

    if ($result = $this->base->query("SELECT * FROM `$table` LIMIT 1")) {
        $fields = $result->fetch_fields();
    } else {
        return false;
        exit();
    }

    foreach ($post as $col => $val) {
        $col = mysql_real_escape_string($col);
        $val = mysql_real_escape_string($val);

        $tmp[$col] = $val;
    }

    if ($custom != null) {

        foreach ($custom as $col => $val) {
            $col = mysql_real_escape_string($col);
            $val = mysql_real_escape_string($val);

            $tmp[$col] = $val;
        }
    }

    foreach ($tmp as $column => $val) {

        foreach ($fields as $fval) {
            if ($column == $fval->name) {

                if ($sep == true) {
                    $query .= ", `$column` = '$val'";
                } else {
                    $query .= "`$column` = '$val'";
                }
                $sep = true;
            }
        }
    }

    return $this->base->query("INSERT INTO `$table` SET " . $query);
    }

    function UpdateValue($post, $table, $custom = null, $where) {

    $query = '';
    $sep = false;
    $tmp = array();

    if ($result = $this->base->query("SELECT * FROM `$table` LIMIT 1")) {
        $fields = $result->fetch_fields();
    } else {
        return false;
        exit();
    }

    foreach ($post as $col => $val) {
        $col = mysql_real_escape_string($col);
        $val = mysql_real_escape_string($val);

        $tmp[$col] = $val;
    }

    if ($custom != null) {

        foreach ($custom as $col => $val) {
            $col = mysql_real_escape_string($col);
            $val = mysql_real_escape_string($val);

            $tmp[$col] = $val;
        }
    }

    foreach ($tmp as $column => $val) {

        foreach ($fields as $fval) {
            if ($column == $fval->name) {

                if ($sep == true) {
                    $query .= ", `$column` = '$val'";
                } else {
                    $query .= "`$column` = '$val'";
                }
                $sep = true;
            }
        }
    }

    return $this->base->query("UPDATE `$table` SET " . $query . " WHERE " . $where);
    }

Ну и остальная часть:

$Core->UpdateValue($_POST, 'cms_articles', null, ' id='.(int)($get_id));	// обновление
$Core->InsertNewValue($_POST, 'cms_articles'); 

 

Заключение

Лично мне очень понравилось работать таким образом с формами (При том что в проекте их было приблизительно: 250).
При необходимости добавления в форму элемента нужно прописать код в шаблоне и добавить значение в базе и все.

Большое спасибо!