Jak vytvořit vlastní formulářový prvek, který bude vracet SplFileInfo?
- jEhLa
- Člen | 70
Dobrý den,
mám komponentu krokového formuláře (wizard) a jedna jeho část slouží pro
nahrání příloh. Mám tedy formulář který obsahuje UploadControl, tím si
nahraji soubory do nějakého temp adresáře. Pomocí Kdyby/Replicator je do
formu přidám (kvůli tomu že nechci v tuto chvíli ještě data
z formuláře nikam odesílat, aby uživatel měl ještě možnost soubor
odebrat). Replicator by však měl obsahovat nový formulářový prvek, který
bude dostávat hodnotu typu \SplFileInfo a vracet do formu \SplFileInfo. To se
mi podařilo nějak zprovoznit, ale jen když byl prvek hidden input a
přenášel jsem v něm řetězec abolutní cesty k souboru, což se mi vůbec
nelíbí, chtěl bych přenášet formem jen název souboru, ale pak výsledný
$form->getValues() vracel \SplFileInfo.
Něco jako UploadControl vrací pole (pokud je multiple) FileUpload.
Snad jsem to popsal srozumitleně. Jak toho docílit?
Ukázka Formu:
protected function createComponentForm()
{
$form = new Form;
//........
$form->addGroup('Přílohy');
$form->addUpload('upload',null,true);
$form->addSubmit('upload_send','Přidat')
->setValidationScope(false)
->onClick[] = $this->uploadSuccess;
$attachements = $form->addDynamic('attachements', function (\Nette\Forms\Container $container) {
$container->addComponent(new Controls\FileHolderInput,'file');
$remove = $container->addSubmit('delete', 'Odebrat')
->setValidationScope(false);
$remove->getControlPrototype()->data['confirm'] = "Opravdu odebrat?";
$remove->onClick[] = $this->removeEvent;
}, 0);
$form->addGroup(null);
$form->addSubmit('submit','Pokračovat')
->onClick[] = $this->formSuccess;
return $form;
}
public function uploadSuccess( \Nette\Forms\Controls\SubmitButton $btn )
{
$files = $btn->getForm()->getValues()->upload;
foreach($files as $file){
if($file->isOk()){
$file->move($this->getPath($this->session_id).DIRECTORY_SEPARATOR.$file->getSanitizedName());
$attachement = $this['form']['attachements']->createOne();
$attachement['file']->setValue( $file->getTemporaryFile() ); // tady bych si přál kdybych už mohl předávat new SplFileInfo($file->getTemporaryFile())
}
}
}
Takže mám zde komponentu „upload“ a tlačítko „upload_send“. To mi zavolá metodu která provede nahrání souboru do temp složky a přidá do Replicatoru. Zde jsem však uvíznul. Potřeboval bych té komponentě předat SplFileInfo (nebo klidně i tu absolutní cestu), ale aby v tom hidden inputu přenášela jen název souboru, bez cesty, komponenta formuláře si nese cestu kde tyto soubory hledat, takže při odeslání formuláře si ověří jestli soubory existují. Výsledek tohoto kroku uloží do session a postoupí na další krok.
Jak by měla taková formulářová komponenta vypadat? Nebo mám režiji nechat na hlavní komponentě a prvkem přinášet opravdu jen název a do session ukládat taky jen název?
Za jakékoliv nakopnutí děkuji.
- jEhLa
- Člen | 70
@akadlec Jojo, to je moje komponenta, která nese hidden input s názvem souboru a zobrazje název souboru jen jako text.
@Milo díky, něco podobného se právě také snažím vytvořit, zkusím prostudovat
Jinak jsem zjistil že komponenta funguje správně, stačilo jen při jejím vytváření jí nastavit kde má soubor hledat:
$container->addComponent(new Controls\FileHolderInput($tempDir),'file');
Ale zjistil jsem že chyba je asi někde jinde. Jakmile uploaduji soubor, přes replicator se mi vytvoří požadovaná komponenta, ale od této chvíle, když kliknu na Pokračovat (submit) se nezavolá požadovaný callback $this->formSuccess (čili nedojde vůbec k uložení hodnot do session). Tuším že to bude asi něco s validací té mé komponenty?
- jEhLa
- Člen | 70
Už fakt nevím, pokud komponenta v metodě getValue vrací řetězec, tak vše funguje v pořádku. Jakmile však vracím SplFileInfo, formulář se nezpracuje. Nějak mi to hlava nebere, nedojde k redirectu, HttpRequest se tváří že jde o POST, ale při refresh stránky prohlížeč neřve že by chtěl znovu odesílat nějaká data :-O
- jEhLa
- Člen | 70
@webdata Ano, přesně to chci. Chci komponentu, která mi bude vracet SplFileInfo, to si uložím do session a když se k té části formuláře někdo vrátí, tak abych té komponentně zase předal jen SplFileInfo.To vím že po odeslání už nejsou, ale při vytváření komponenty formuláře a formulářových prvků by se měli znovu nastavit. Jako například UploadControl (tomu se však logicky nedá nastavit hodnota přes setValue) vrací instanci FileUpload.
- webdata
- Člen | 153
jEhLa napsal(a):
ale při vytváření komponenty formuláře a formulářových prvků by se měli znovu nastavit.
Z čeho se mají má znovu nastavit? Kde získají tu informaci podle které se má SplFileInfo znovu nastavit? Funguje to asi takhle.
1/ Přijmu data z formuláře,
2/ Vytvořím instance SplFileInfo
3/ Vygeneruji formulář s inputama obsahující ukazatel na instance
SplFileInfo
4/ Formulář se zobrazí uživateli
V tuto chvíli už neexistují předešlé instance SplFileInfo a proto ty
inputy obsahující ten ukazatel
jsou na nic.
Tím že udělám:
$inst = new SplFileInfo....;
$form->addHidden('foo', 'foo')
->setValue($inst);
Tak nepředávám celou třídu.
Možná by fungovalo něco jako (nezkoušel jsem):
$inst = new SplFileInfo....;
$form->addHidden('foo', 'foo')
->setValue(serialize($inst)); // <<<--- EDIT!!! TOHLE JE BLBOST A NEFUNGUJE TO!!!
… ale tohle už mi připadá jako prasárna.
Editoval webdata (20. 8. 2015 8:45)
- jEhLa
- Člen | 70
@webdata Ta komponenta FileHolderInput($tempDir) při vytváření dostává v $tempDir adresu kde soubory hledat. setValue() té komponenty přijímá string nebo SplFileInfo, pokud se jedná o string, nejdřív zkusím jestli to není absolutní cesta k souboru, pokud ano, vytvořím SplFileInfo, pokud ne, použiju property $tempDir a v ní zkusím dohledat soubor podle řetězce, když najdu, vytvořím opět SplFileInfo.
To mi funguje, jen řeším proč nemůže getValue() vracet SplFileInfo?
Při POSTu formuláře chápu že ten objekt nikde nemám, proto nastavuji
hodnotu podle řetězce a objekt si vytvoří samotná komponenta. getControl()
jako value nese jen název souboru.
Chtěl jsem aby v callbacku po odeslání formuláře, když zavolám
$form->getValues() ta komponenta vrátila SplFileInfo.
Ukázka té komponenty. Není to moc hezky napsaný, ale dělám na tom pokusy.
class FileHolderInput extends \Nette\Forms\Controls\HiddenField
{
protected $path;
protected $file = null;
public function __construct( $path )
{
parent::__construct();
$this->path = $path;
}
public function getControl()
{
$file = $this->file;
if($file !== null){
$filename = \Nette\Utils\Html::el('span',['class'=>'filename'])->setText($file->getBasename());
$filesize = \Nette\Utils\Html::el('span',['class'=>'filesize'])->add(\Nette\Utils\Html::el('small')->setText(' ('.round($file->getSize()/1024,1).'kB)'));
//$filetype = \Nette\Utils\Html::el('span',['class'=>'filetype'])->setText(finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file->getPathname()));
$fileicon = \Nette\Utils\Html::el('i',['class'=>'glyphicon glyphicon-file']);
$fileinfo = \Nette\Utils\Html::el('div',['class'=>'fileinfo'])->add($fileicon)->add($filename)->add($filesize);
return \Nette\Utils\Html::el()->add($fileinfo)->add(parent::getControl());
}
}
public function setValue($value)
{
if($value instanceof \SplFileInfo){
$this->file = $value;
parent::setValue($value->getBasename());
}
elseif(is_file($value)){
$this->file = new \SplFileInfo($value);
parent::setValue($this->file->getBasename());
}
elseif(is_file($this->path.DIRECTORY_SEPARATOR.$value)){
$this->file = new \SplFileInfo($this->path.DIRECTORY_SEPARATOR.$value);
parent::setValue($value);
}
}
public function getValue()
{
//toto je to co řeším, proč tu nemůžu mít jen return $this->file?
if($this->file instanceof \SplFileInfo){
return $this->file->getPathname();
}
return null;
}
public function isFilled()
{
return $this->getValue() !== null;
}
}
Editoval jEhLa (20. 8. 2015 11:01)
- webdata
- Člen | 153
No tak to je jasny proc to nejde ne?
1/ setValue nastavuje hodnotu toho tveho kontrolu. Tim tam da i to
SplFileInfo ale pouze
do instance toho tveho kontrolu, ktera hned umre po vykresleni formulare.
2/ Po odeslani formulare pak nacitas getValue jenze vytvaris novou instanci
objektu tveho kontrolu
a ta uz nema instanci SplFileInfo.
Takze pokud by jsi chtel nacitat getValue, tak pred tim musis nejak udelat setValue.
Takze bych radeji to co mas v setvalue dal do getvalue a tam z nazvu
souboru vracel SplFileInfo
z hidden retezce, který ukazuje na soubor.
A jinak me nepripada nejak cool posilat k uzivateli v hidden cestu kam ukladas soubory na serveru.
- jEhLa
- Člen | 70
Po odeslání formuláře to ještě přeci musí projít validací, takže se komponenta znovu vytvoří a znovu se setValue()? Nebo se pletu? Když si dumpnu setValue() tak se po odeslání formuláře provádí myslím hned 3×.
Však neposílám cestu (proto to celé řeším), getControl() vrací hidden input pouze s názvem souboru, né cestu. Cestu zná jen ta komponenta, ale nikde ji nevypisuje.
>Takze bych radeji to co mas v setvalue dal do getvalue a tam z nazvu
souboru vracel SplFileInfo
z hidden retezce, který ukazuje na soubor.
To ale právě říkám že nejde. Jakmile do getValue() dám return new SplFileInfo($pathname), žádná data se nepřenesou.
- webdata
- Člen | 153
jEhLa napsal(a):
Po odeslání formuláře to ještě přeci musí projít validací, takže se komponenta znovu vytvoří a znovu se setValue()? Nebo se pletu? Když si dumpnu setValue() tak se po odeslání formuláře provádí myslím hned 3×.
Však neposílám cestu (proto to celé řeším), getControl() vrací hidden input pouze s názvem souboru, né cestu. Cestu zná jen ta komponenta, ale nikde ji nevypisuje.
>Takze bych radeji to co mas v setvalue dal do getvalue a tam z nazvu souboru vracel SplFileInfo
z hidden retezce, který ukazuje na soubor.To ale právě říkám že nejde. Jakmile do getValue() dám return new SplFileInfo($pathname), žádná data se nepřenesou.
Jo jenze ja jsem nepsal ze getValue bude vracet SplFileInfo které tam nastaví SetValues(), ale bude vracet SplFileInfo z jmena souboru které nastaví SetValue().