Pluupload, uložení dat do db

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
danielseek
Člen | 42
+
0
-

Jedna věc nahrát soubory někam na web, druhá uložit jejich adresy do db a k tomu ideálně nějaké další informace jako jméno obrázku, příslušnost do galerie apod. Jaká je nejlepší metoda jak tohle řešit?

Za všech okolností musím využít následující callback.
$uploader->…->setOnSuccess(callback($this, ‚fileUploaded‘));

Myšlenka byla taková, že v okamžiku kdy je jeden z vícero souborů nahrán, zavolá se tento callback a do formuláře se doplní náhled obrázku spolu s nějakými inputy.

Můj hlavní problém nyní je, že nevím jak bych zajistil perzistenci dat v rámci ajaxu. Podařilo se mi, aby se přidal pokaždé jeden řádek, ten se však při dalším vykonání callbacku přemaže místo aby se appendoval za ten stávající. Zkoušel jsem to zařídit pomocí anotace persitent pro celou komponentu formuláře, ale bezvýsledn. V následujícím kódu jsem ověřoval, jestli by nepomohlo pokaždé formulář znova vytvářet z dat přenášených v proměnné $uploadedImages.

kód v presenteru:

   /** @persistent */
   public $uploadedImages = Array();
   /** @persistent */
   public  $imageCounter = 1;
/** @persistent(imageForm) */
   public function createComponentImageForm($name){
	$f = new Form;
	$f->getElementPrototype()->addAttributes(Array("class"=>"formee"));
	$f->addText("name", "Název galerie");
	$f->addSubmit("send", "Vytvořit galerii a uložit soubory");
	$f->onSuccess[] = $this->imageFormSubmitted;
	return $f;
   }

   public function createComponentPlupload()
   {
	// Main object
	$uploader = new \Echo511\Plupload\Rooftop();

	// Use magic for loading Js and Css?
	$uploader->disableMagic();

	// Configuring paths
	$uploader->setWwwDir(WWW_DIR) // Full path to your frontend directory
		 ->setBasePath($this->template->basePath) // BasePath provided by Nette
		 ->setTempLibsDir(WWW_DIR . '/tools/PluUpload'); // Full path to the location of plupload libs (js, css)

	// Configuring plupload
	$uploader->createSettings()
		 ->setRuntimes(array('html5',  'flash')) // Available: gears, flash, silverlight, browserplus, html5
		 ->setMaxFileSize('1000mb')
		 ->setMaxChunkSize('2mb'); // What is chunk you can find here: http://www.plupload.com/documentation.php

	// Configuring uploader
	$uploader->createUploader()
		 ->setTempUploadsDir(WWW_DIR . '/tools/PluUpload/temp') // Where should be placed temporaly files
		 ->setToken("PU") // Resolves file names collisions in temp directory
		 ->setOnSuccess(callback($this, 'fileUploaded')); // Callback when upload is successful: returns Nette\Http\FileUpload

	return $uploader->getComponent();
   }

   public function fileUploaded(\Nette\Http\FileUpload $fileUpload){
	if($fileUpload->isImage()) {

	    $name = $fileUpload->getSanitizedName();
	    $path = "/upload/img/".$name;
	    $this->uploadedImages[] = Array("name"=>$name,"path"=>$path);
	    foreach($this->uploadedImages as $image) {
		$this["imageForm"]->addHidden("imageSrc".$this->imageCounter)->setDefaultValue($image["path"]);
		$this["imageForm"]->addText("imageName".$this->imageCounter, "Název obrázku")->setDefaultValue($image["name"]);
		$this["imageForm"]->addCheckbox("imageIsPreview".$this->imageCounter, "Nastavit jako náhled celé galerie?");
	    }
	    ++$this->imageCounter;
	    $this->invalidateControl("form");
	    return $fileUpload->move(WWW_DIR.$path);
	} else return false;
   }

kód v šabloně:

	{form imageForm}
		<h3>Nahrajte soubory</h3>
		{control plupload}
		<h3>Informace o souborech</h3>
		<table>
		     {snippet form}
			    {var $i = 1}
			    {while isset($_form["imageSrc".$i])}
				{var $formElements = Array(
				    "imageSrc","imageName","imageIsPreview"
				);}
				<tr>
				    <td>
					<img src="{$_form["imageSrc".$i]->getValue()}" width="200">
				    </td>
				    {foreach $formElements as $name}
					<th>{label $name.$i /}<th>
					<td>{input $name.$i}</td>
				    {/foreach}
				</tr>
				{var $i++}
			    {/while}
			{/snippet}
		</table>
		<h3>Informace o galerii</h3>
	    <table>
		{var $formElements = Array(
		    "name","send"
		);}
	    <tr n:foreach="$formElements as $name">
		<th>{label $name /}<th>
		<td>{input $name}</td>
	    </tr>
	    </table>
	{/form}

Editoval danielseek (25. 3. 2013 0:50)

echo
Člen | 134
+
0
-

Zdravím
Plupload pracuje tak, že nahrává soubor ve více requestech (chunking). Když Plupload odešle na aplikaci stop parametr, aplikace ví, že už nepřijdou žádná nová data a zavolá onSuccess[].

Persistentní parametry fungují pouze, pokud se mění url – u ajaxu logicky k redirectu nedochází.

Ty potřebuješ něco, co funguje skrz několik instancí aplikace (několik requestů) – fyzicky potřebuješ někam uložit stav aplikace, tj. které soubory jsou již uploadovány. Proměnné objektu nelze z logiky php použít ⇒ nutno využít cache, session, etc.

danielseek
Člen | 42
+
0
-

Děkuji za odpověď, jak plupload pracuje jsem si zjišťoval, nicméně bych potřeboval vědět jestli můžu nějakým způsobem zjistit k jaké frontě soubor patří.

echo
Člen | 134
+
0
-

Nastudovat Plupload, zjistit, jakým způsobem nastavit, aby odeslal krom dat souboru podsunutý parametr, upravit šablonu a hotovo.

Tuším, že podobně je vyřešeno předávání tokenu – aby bylo možno nahrávat soubory ze dvou a více komponent současně a přitom aby používaly stejný temp pro chunking.

Pokud by ti stačil ten token, tak si udělej továrničku, která ti komponentu bude vytvářet a k instancím pak přistupuj přes ni.

class Factory
{
	private $instances = array();

	public function createInstance($token)
	{
		$this->instances[$token] = $this->createPlupload($token);
	}

	public function getInstance($token)
	{
		return $this->instances[$token];
	}

	protected function createPlupload($token)
	{
		...
	}
}

Chtělo by to refactoring a přidání pár eventů pro lepší manipulaci.

danielseek
Člen | 42
+
0
-

omlouvám se za svou nechápavost, jsem v nette začátečník, k čemu by byla dobrá ta factory, kterou jste načrtnul? Nepotřebuji spíš něco podobného pro komponentu imageForm, do které přidávám ona pole a je proto potřeba její část uložit do cache? Což by se zohlednilo v CreateImageForm.

Jak se bude chovat callback pro zpracování formuláře, když tam ajaxem na tvrdo přidám další inputy o kterých komponentá neví?

echo
Člen | 134
+
0
-

Pro jeden formulář a jednu instanci Pluploadu bych řešil problém takto:

class UploadedQueue extends Object
{

	private $session;

	public function __construct(Nette\Http\Session $session)
	{
		$this->session = $session->getSection("uploadedQueue");
	}

	public function addFinishedUpload(\Nette\Http\FileUpload $file, $token = 0)
	{
		$this->session->uploads[$token] = $file;
		return $this;
	}

	public function getFinishedUploads($token = 0)
	{
		return $this->session->uploads[$token];
	}

}


class PluploadProcessor extends Object
{

	private $queue;

	public function __construct(UploadedQueue $queue)
	{
		$this->queue = $queue;
	}

	public function addFinishedUpload(\Nette\Http\FileUpload $upload, $token)
	{
		$this->queue->addFinishedUpload($upload, $token);
	}

	public function fillForm(\Nette\Application\UI\Form $form, $token)
	{
		$container = new \Nette\Forms\Container;

		foreach($this->queue->getFinishedUploads() as $upload) {
			$container->add(...)
		}

		$form->addContainer($container);
	}

}

class Presenter extends \Nette\Application\UI\Presenter
{

	private $pluploadProcessor;

	public function injectPluploadProcessor(PluploadProcessor $pluploadProcessor)
	{
		$this->pluploadProcessor = $pluploadProcessor;
	}

	public function createComponentForm()
	{
		$form = new \Nette\Application\UI\Form;
		$form->add....
		return $form;
	}


	public function createComponentPlupload()
	{
		$plupload = new ....
		$plupload->setOnSuccess(callback($this, 'onFileUploaded'));
		return $plupload;
	}


	public function onFileUploaded(\Nette\Http\FileUpload $upload)
	{
		$this->pluploadProcessor->addFinishedUpload($upload, 'myToken');
		$this->pluploadProcessor->fillForm($this['form'], 'myToken');

		$this->invalidateControl(); // ajax
	}

}

Samozřejmě to berte jako náčrt. V případě potřeby více instancí by se hodila i ona továrnička na vytváření instancí dle tokenu, kterou jsem načrtl dříve.

Poznámka: Aplikační/datová logika by neměla být řešena v Presenteru. Presenter by neměl data kamkoliv ukládat. Připomínám vzhledem k vašemu kódu v úvodním příspěvku.

Editoval echo (26. 3. 2013 22:27)

danielseek
Člen | 42
+
0
-

Skutečně jste mi pomohl. Děkuji za rozsáhlou odpověď.

Editoval danielseek (26. 3. 2013 23:54)

danielseek
Člen | 42
+
0
-

Ještě jeden doplňující dotaz nebylo by vhodnější posílat ajaxem jen právě přidaný soubor než invalidovat pokaždé celý formulář (nebo jeho valnou část).

echo
Člen | 134
+
0
-

Invalidovat jen to, co se přidává. Jinak se celý formulář pokaždé přemaže, což není moc user-friendly.

danielseek
Člen | 42
+
0
-

Takže máte na mysli dynamické snippety? To je dost ošemetné ne? Nebo se vyplatí napsat si vlastní způsob předávání dat v jsonu? Vzhledem k tomu, že je potřeba předat jen dvě proměnné je to asi docela moudré…

Editoval danielseek (1. 4. 2013 20:25)