Алгоритм работы с множеством прикреплённых файлов

aaachilov

Новичок
Приветствую!
Люди добрые подскажите как лучше сделать.
При добавлении материалов к ним прикрепляются файлы.
Например я в настройках ставлю что максимальное колличество прикрепляемых файлов 10 и у меня в форме добавления становится возможно загрузить 10 файлов - заранее я сделал 10 полей для тих фалов в базе
Сейчас у меня например 10 фалов и 10 полей в таблице - но если я поставлю 12 файлов - полей уже не будет - да и для каждого файла отдельная ячейка получается.
Как это дело можно оптимизировать?
И возможно ли сделать что бы сколько бы файлов я не загрузил они сохранились в базе - или для каждого нужно делать предварительно ячейку?
Заранее спасибо!
 

С.

Продвинутый новичок
Не нужно ячеек. Подобная информация хранится в отдельной таблице, один файл -- одна запись. Логическая связь между двумя таблицами "один ко многим". То есть одной записи в основной таблице соответствую 1,2,10,12 или 120 записей во второй,
 

aaachilov

Новичок
То есть делаю отдельную таблицу (id, id материала, адрес файла, размер и название и формат например)- из формы получаю массив с файлами - в цикле их записываю во вторую таблицу. а когда нужно получить файлы - в моём случае все через шаблонизатор работает - просто выбираю из базы строки где совпадает id материала и также в цикле обратно присваиваю адреса тегам напрмиер {image1},{image2},{image3} и так далее. Такая схема верная будет?
 

aaachilov

Новичок
Чего то помоему ничего хорошего с такой идеи с отдельной таблицей не получается.
Лишние запросы к базе или у меня руки кривые. Как мне во вторую таблицу передать идентификатор материала, если материал ещё не записан? Сначало записать материал потом файлы? А если файлы неудачно запишутся, что материал удалять?
Помоите плиз разобраться. Скрипт я написал и он работает - но получается куча кода и лишнее обращение к базе
Заранее благодарю
 

fixxxer

К.О.
Партнер клуба
Сначало записать материал потом файлы? А если файлы неудачно запишутся, что материал удалять?
да

можно не удалять, а ставить флаг скажем is_temporary - тут добавляется юзабилити, можно сделать продолжение редактирования неоконченного материала
 

aaachilov

Новичок
Тогда еси не сложно ещё намекните - записал я данные в таблицу с материалом - а как лучше получить обратно id материала - а то у меня сделано что я после записи выгружаю материал с максимальным значением поля id и этот id использую в виде идентификатора принадлежности файлов во второй таблице.
Заранее спасибо!
 

fixxxer

К.О.
Партнер клуба
ужас

если mysql - auto_increment, mysqli_insert_id

псевдокодом так
PHP:
$db->begin()
try {
   $db->query("insert into article ...")
   $id = db->insert_id()
   $db->query("insert into images ( ) values ( $id, ...) , ( $id, ...) , ( $id, ...)")
   move_uploaded_files_to_storage()
   $db->commit()
}
catch (exception $exception) {
   @$db->rollback() // не должно выбрасываться исключений
}
if (isset($exception)) {
    delete_uploaded_files_from_storage()
    throw $exception
}
return $id
 

aaachilov

Новичок
Все - вроде работает - но если например добавить к материалу 30 картинок то получается что нужно сделать 31 запрос к базе
Так и должно быть?

Я просто думаю если адреса изображений хранить в той же базе что и материал - даже если заранее сделать 30 полей для них - то получится 1 запрос к базе - та не быстрее получится?
Заранее спасибо!

Если что вот код целиком
PHP:
@session_start ();
if (empty($_POST['sub_nm'])) die ('HERE IT IS IMPOSSIBLE :-)');
define ('HAKKING', TRUE);
// подключаем настройки
require_once($_SERVER['DOCUMENT_ROOT']."/system/config/options_articles.php");
// определяем максимальное количество файлов к материалу
$max_files = $options_mod['max_files'] - 1;
// фильтруем входящие данные
foreach($_POST as $key => $value) {  
    $value=trim($value);
    if (get_magic_quotes_gpc()) $value = stripslashes($value);
    $value=htmlspecialchars($value,ENT_QUOTES);
	$value = strip_tags($value);
    $_POST[$key]=$value;
    $value=str_replace("\r","",$value);
    $value=str_replace("\n","<br>",$value);
    $msg[$key]=$value;
  } 
// получаем значения переменных из массива POST
if (isset($_POST['title'])){$title = $_POST['title'];}
if (isset($_POST['sec'])){$sec = $_POST['sec'];}
if (isset($_POST['cat'])){$cat = $_POST['cat'];}
if (isset($_POST['meta_d'])){$meta_d = $_POST['meta_d'];}
if (isset($_POST['description'])){$description = $_POST['description'];}
if (isset($_POST['text'])){$text = $_POST['text'];}
if (isset($_POST['meta_k'])){$meta_k = $_POST['meta_k'];}
if (isset($_POST['author'])){$author = $_POST['author'];}
$date = date("Y-m-d H:i:s");

// записываем материал в базу
 $ruse = $db->query ("INSERT INTO `data_articles` (`sec`,`title`,`cat`,`meta_d`,`description`,`content`,`meta_k`,`author_name`,`date`) 
VALUES ('".$sec."', '".$title."', '".$cat."', '".$meta_d."', '".$description."', '".$text."', '".$meta_k."', '".$author."', '".$date."')");
// получаем идентификатор материала
$f_a_id = $db->insert_id();
// записываем изображения в базу и сохраняем их на сервере
	for ($i = 0; $i <= $max_files; $i++) {
		if(empty($_FILES['userfile']['name'][$i])) break;	
		$file = $_FILES['userfile']['tmp_name'][$i];
		$filename = $_FILES['userfile']['name'][$i];
		ini_set('memory_limit', '32M'); 
		$size = filesize ($_FILES['userfile']['tmp_name'][$i]); 
		$type = strtolower(substr($filename, 1+strrpos($filename,".")));
		$new_name = 'file-'.time().$i.'.'.$type; 
		copy($file, "../../upload/articles/".$new_name);
		$mini_img = "/upload/articles/$new_name";
	$rusu = $db->query ("INSERT INTO `files_articles` (`f_a_id`,`f_name`,`f_size`,`f_type`) VALUES ('".$f_a_id."','".$_FILES['userfile']['name'][$i]."', '".$size."', '".$mini_img."')");
	}
// в случае неудачной записи	
	if ($ruse == false or $rusu == false) {
//выбираем файлы для удаления
	$e = $db->query("SELECT `f_type` FROM `files_articles` WHERE `f_a_id`= '$f_a_id' ");
// удаляем файлы с сервера	
while($res = $db->fetch_array($e)){
	unlink($_SERVER['DOCUMENT_ROOT']."$res[f_type]");
	}
// удаляем материал с базы	
		$db->query ("DELETE FROM `data_articles` WHERE id='$f_a_id'");
// удаляем файлы с базы
		$db->query ("DELETE FROM `files_articles` WHERE f_a_id='$f_a_id'");
		
	

		
		echo "<center>Материал не добавлен!<br><br><input name='back' type='button' value='Вернуться назад' onclick='javascript:self.back();' id='deltr1'/></center>";	
	} else {
		echo "<center>Материал успешно добавлен<br><br><form action='/admin.php?mod=articles&action=new_nm' method='post'><input type='submit' value='Добавить ещё' id='deltr1'></form></center>";
	}
 

A1x

Новичок
даже если заранее сделать 30 полей для них
это типичное решение как обычно делают индусы

правильно сделать две таблицы и отношение один-к-многим, как тебе посоветовали выше
fixxxer написал тебе как примерно может выглядеть хороший код и как вставить несколько записей одним запросом

да даже 30 запросов в данном случае не было бы проблемой
 

С.

Продвинутый новичок
Во-первых, можно написать один запрос, вставляюший 30 записей. А во-вторых, одна такая "тажелая" запись приходится на многие тысячи посещений, что практически не влияет на общую картину загрузки.
 

aaachilov

Новичок
Да Вы правы! Сейчас с безопасностью поковыряюсь и думаю пока пойдёт. А чтобы в один запрос 30 записей запихать нужно сначало получить переменные - так что лучше в цикле наверное да и заранее не известно сколько файлов придёт. Или хотя если помудрить можно в самом запросе наверное цикл запустить....
 

aaachilov

Новичок
Ещё раз зашёл в тупик, все по этой же теме. Не могу придумать алгоритм работы. У меня каждая загруженная картинка получает альтернативное имя такого вида {IMG_№} для того чтобы парсер её заменял на код изображения. То есть при добавлении материала я например этот тег просто вставляю в текст и т.п. но столкнулся с такой проблемой. Например есть 3 изображения которым присвоено {IMG_1} {IMG_2} {IMG_3} они присваиваются в порядке добавления и выгружатся потом через парсер в таком же порядке - но если я удалю например второе изображение у меня третье станет вторым, а в тексте например вставлен тег третьего и т.п.
Думал хранить имя такого вида в базе и потом уже при выгрузке присваивать ему адрес картинки в цикле - но опять же не понимаю как это сделать.
Если например я получаю при добалении материала три изображения - присваиваю им {IMG_1} {IMG_2} {IMG_3} и пишу в базу - все отлично - но если я потом ещё решу добавить к этому же материалу изображения - как мне понять что оно должно быть четвёртым?
Вроде подробно объяснил.
Помогите советом.
Заранее спасибо!
 

Ragazzo

TDD interested
обычно уникальные имена изображениям дают вида время+размер или md5
 

aaachilov

Новичок
Именно имя вида file-datetime+[номер в массиве] используется для адреса изображения - но для того чтоб это избражение понял (шаблонизатор) у него должно быть имя {IMG_№} где № это идентификатор изображения в зависимости от кторого парсер присваивает тегу адрес изображения. И самое что не могу понять это то как сдеать уникальность этих идентификаторов, при условии что для каждого материала он дожен начинаться с 1
 

С.

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