FORM API probléma

pero képe

Sziasztok!

Elakadtam. Szeretnék segítséget kérni FORM API-val kapcsolatban.

A probléma leegyszerűsítve:

Van egy "munkaajanlat" nevű tartalomtípus a köv mezőkkel:

  • Title
  • 'Státusz' taxonomy szótár, mely szerint 'Felvitt' vagy 'Elvállalt' lehet egy munkaajánlat
  • + egyéb mezők, amik a problémával kapcsolatban irrelevánsak
  • A felvitt ajánlatok 'Felvitt' státuszt kapnak. A munkavállalok bejelentkeznek és kapnak egy listát a 'felvitt', azaz elvállalható ajánlatokból. Ez a lista gyakorlatilag generált FORM-ok. A FORM tartalmazza hidden input elemben az ajánlat NID-jét és egy 'Elvállalom' submit gombot.
    Ha a user rákattint az 'Elvállalom' gombra akkor a myform_form_submit() függvényben átírom az adott NID-ű ajánlatnak taxonomy term-jét 'Elvállalt' értékre.

    Ez eddig kiválóan működik is.

    Viszont lehet olyan eset, hogy egyszerre 2 user is be van jelentkezve és mindegyik egyszerre betölti a listát, majd az egyik az 'Elvállalom' gombra kattint, majd pár mp után a másik is, mivel még ott van a listájában az adott ajánlat.

    Ebben az esetben ki szeretném írni a user-nek, hogy elkésett, más már elvállalta előtte.
    Úgy gondoltam, hogy ez roppant egyszerűen megoldható úgy, hogy myform_form_submit()-ban mielőtt státust váltok megvizsgálom, hogy nem volt-e már státuszváltás. Ha volt akkor üzenetben (drupal_set_message) értesítem az elkésett usert.
    A gond az, hogy az elkésett user, ha az 'elvállalom' gombra kattint, akkor nem jutok el a myform_form_submit() függvénybe, azaz nem történik semmi. Egyszerűen újratöltődik az oldal a már elvállalt ajánlatok nélkül. De akkor így hogy tudassam a userrel hogy elkésett?

    Köszönöm.

    Drupal verzió: 
    szantog képe

    Validate, amit keresel: https://api.drupal.org/api/drupal/developer%21topics%21forms_api_referen...
    A myform_form_validate()-ben csinálod meg ugyanazt a vizsgálatot, ami most a form submitban van. Ha közben elfogadták az ajánlatot, a validate az egész form_submit folyamatot megszakítja, ha form_set_error-t állítasz rá - és az üzeneted is kész.

    0
    0

    ----
    Rájöttem, miért kérdezek olyan ritkán a drupal.hu-n. Amíg szedem össze az infokat a kérdéshez, mindig rájövök a megoldásra.

    pero képe

    Ezzel próbálkoztam, hogy a validate lefut-e, de sajnos ez sem.
    Úgy ellenőriztem, hogy csak egy drupal_set_message('mizú?')-t raktam a validate függvénybe. Az első usernél volt üzenet, a második elkésett user nem kapott mizú? üzenetet. Így úgy gondolom, hogy nem jutott el se a myform_form_validate()-be és se a myform_form_submit()-ba.

    0
    0
    pero képe

    Kicsit részletesebben leírom, hogy hogyan csináltam a dolgot, hátha koncepcionálisan rontottam el valamit :)

  • Hook_menu()-vel csináltam egy "page callback"-et.
  • A "page callback"-ben megadott függvényben EntityFieldQuery()-vel lekérdezem az összes ajánlatot, ami "felvitt" státuszban van.
  • Az eredményt szépen táblázatos formában megjelenítem, és mindegyikhez csatolok egy formot drupal_get_form()-al, a form neve legyen itt most myform.
  • Mivel egy oldalon így több form lesz, azt hogy az FormID-jűk ne legyen azonos, a hook_form()-al oldom meg: a formID végéhez hozzáírom az ajánlat NID-jet, hook_form()-ban minden FormID esetén callback-nek a myform_form()-ot adom meg
  • A myform_form()-ban definiálok egy hidden típusú mezőt az adott ajánlat NID-jével és egy submit gombot.
  • Végül megadom a myform_form_submit()-t függvényt, ahol a státuszt módosítom 'elvállalt'-ra. És itt szerettem volna viszgálni, hogy a státuszváltas megtörtént-e már, de ha már megtörtént, nem jutok el idáig, és a myform_form_validate()-ig se.
  • 0
    0
    szantog képe

    "Mivel egy oldalon így több form lesz, azt hogy az FormID-jűk ne legyen azonos, a hook_form()-al oldom meg: a formID végéhez hozzáírom az ajánlat NID-jet, hook_form()-ban minden FormID esetén callback-nek a myform_form()-ot adom meg
    A myform_form()-ban definiálok egy hidden típusú mezőt az adott ajánlat NID-jével és egy submit gombot."

    Itt már van némi zavar van az erőben, a hook_form nem erre való. Neked nem kell foglalkozni a form_id-vel, a form api lekezeli, hogyha egy form_id-jű formot többször jelenítesz meg, az ne legyen gond.

    A nidet simán drupal_get_form('my_form', $nid)-ként add át, a formodban ezt a nidet megtalálod a $form_state['build_info']['args'][0] helyen, vagy még jobb, ha drupal_get_form('my_form', array('nid' => $nid))-ként hívod, és még jobban látszik majd a build infoban, hogy ez egy nid. (['build_info']['args'][0]['nid'])

    Nézzük meg, ezután hogy állsz, mert simán el tudom képzelni, hogy a hook_form vudu borítja az egész processt, főleg, hogy ezzel még jóformán az összes node_edit formba sikerült beletrollkodni. :)

    Amúgy én ezt az egészet flag + rules-szal csinálnám, kell egy flag, hogy accept_offer, kell egy rule a content is flagged accept_offer eseményre, ahol a condition valami ilyesmi, hogy node-flag-counter > 0, action meg drupal_set_message + unflag node. Így mindig egy darab nodeod lesz flaggelve, és készen kapsz egy ui-t az ajánlat visszavonására. A page-t meg megcsinálod views-al.

    0
    0

    ----
    Rájöttem, miért kérdezek olyan ritkán a drupal.hu-n. Amíg szedem össze az infokat a kérdéshez, mindig rájövök a megoldásra.

    pero képe

    Legelőszőr úgy csináltam, ahogy írtad, nem foglalkoztam a FormID-el. Viszont ebben az esetben mindig az oldalon lévő legelső form lett elküldve, teljesen mindegy melyik submit gombra kattintottam, ezért jött be a hook_form(), mégpedig így:

    function modulename_forms($form_id, $args) {
      $forms = array();
      // ha a formID: myform_form_xxx
      if (substr($form_id, 0, 12) == 'myform_form_') {
        $forms[$form_id] = array('callback' => 'myform_form');
      }
      return $forms;
    }

    Ezzel a többi node_edit form-ba (szerintem) nem trollkodtam bele :)

    A NID-et (is) amúgy átadom a drupal_get_form-nal, hogy tudjak értéket adni a hidden field-nek. De ettől még azonosak lesznek a FormID-ek.

    Flag modult még nem használtam, de úgy nézem hogy ez csak igen/nem lehetőségre jó. Az én feladatom ennél viszont komplexebb, az itt felvázolt rész csak egy töredéke. Nem csak ez a két ajánlat státusz van hanem jóval több.
    A page generálása is bonyisabb, írtó sok field-em van + field collectionok, pláne field collectionokban is vannak field collectionok + if feltételek hogy mikor mit írjak ki :) Szóval elég egyedi a listázási mód, amit views-al nem tudok megoldani szerintem.

    0
    0
    szantog képe

    Ohh, emlegetted ezt a hook_form-t, közben a hook_forms használtad, két nagyon külön dolog ám, szóval amit ott csináltál, az teljesen rendben van. Akkor már csak az a kódrészlet kell, ahol drupal_get_form()-al összeszeded a formot.

    0
    0

    ----
    Rájöttem, miért kérdezek olyan ritkán a drupal.hu-n. Amíg szedem össze az infokat a kérdéshez, mindig rájövök a megoldásra.

    pero képe

    óó, bocsánat, ezt valóban összekavartam egy kicsit.

    A drupal_get_form:
    (ezt hívom meg a "page callback" függvényben többször.)

    drupal_render(drupal_get_form('order_undertake_form_'.$order_items[$nid]->nid,$order_items[$nid]->nid,$order_items[$nid]->field_rendeles_felrakas[LANGUAGE_NONE][0]['value'],$order_items[$nid]->field_rendeles_felrakas_datuma[LANGUAGE_NONE][0]['value'],$order_items[$nid]->field_rendeles_surgos[LANGUAGE_NONE][0]['value'],$ar))

    Azaz a Form id az: order_undertake_form_[nid]
    plusz átadok egyéb paramétereket is.

    A hook_form() pedig elég szokványos:

    function order_undertake_form($form, &$form_state, $ordernid, $hely, $datum, $surgos, $ar) {
      if ($surgos)
        $form['surgos'] = array(
          '#markup' => '<div class="surgos">'.t('Urgent').'</div>',
          );
     
     $form['upload_date'] = array(    
      '#type' => 'date_popup', 
      '#default_value' => $datum,
      '#date_format' => 'Y-m-d',
      '#required' => TRUE, 
      '#tree' => TRUE,
      );	
     
      $form['felrakas'] = array(
        '#markup' => '<div class="felrakashely">'.$hely.'</div>',
      );
     
      $form['pricemarkup'] = array(
        '#markup' => '<div>Fuvar költség:</div><div class="ar">'.number_format($ar, 0, ',', ' ').' EUR</div>',
      );
     
      $form['ordernid'] = array('#type' => 'hidden', '#value' => $ordernid);
      $form['price'] = array('#type' => 'hidden', '#value' => $ar);
     
      $form['submit'] = array(
        '#prefix' => '<div class="submit">',
        '#sufix' => '</div>',
        '#value' => t('Elvállalom'),
        '#type' => 'submit', 
      );	
      return $form;
    }
    0
    0
    pp képe

    Tehát már form sincs, ha elvállalták?

    Az a helyzet, hogy amikor a második ember ráklikkel a form-ra akkor a böngésződ újratölti az oldalt (POST), ami ugye legenerálja a formokat azokhoz az elemekhez amiket még nem vállaltak el, de ugye amit már elvállaltak, ahhoz nem, ezért nem is fut le a validate hook-ja annak a formnak ami nincs is már.

    Talán ha mindegyik elemhez raksz formot, csak amelyik már elvállalt, annál nem raksz submit gombot, vagy access false-ra rakod akkor már a validate függvény le fog futni. (vagy ha nem akkor a submti gombok divjébe egy display:none tud segíteni. :))

    Praktikusabb lenne egy űrlap sok submit gombbal, ahol a gomb id-jébe kódolod a nid-et. Vagy még tovább menve, miért is kell Form ehhez? elég lenne egy link + token szerintem.

    pp

    0
    0
    pero képe

    Azt mondod, hogy amikor elküldök egy form-ot, akkor a böngésző először újratölti azt, majd utána küldi el a POST változókat? Én úgy gondoltam, h ez fordítva van.

    Form azért kell, mert az elvállal gomb megnyomása előtt a user módosíthat egy az ajánlathoz csatolt date mezőt.

    Gondolkodom azon amiket írtál, köszönöm.

    0
    0
    pp képe

    Nem, nagyon rosszul fogalmaztam, ha így értetted, megpróbálom világosabban:

    Az űrlap feldolgozás úgy néz ki, hogy:

    Jött-e adat?
    igen
    Valid-e?
    igen
    Feldolgozom | Ha nemjött, vagy nem valid akkor kiíratom a formot.

    Valami ilyesmi van a drupal_get_form fv működése mögött. Mivel Te csak azokhoz a ajánlatokhoz gyártasz űrlapot amelyiket még nem fogadták el, ezért Te a fenti mechanizmust be se indítod, ezért azok az adatok nem is kerülnek feldolgozásra. Szóval a drupal_get_form az nem csak az űrlapot generálja le, hanem végighívogatja a ellenőrző és a feldolgozó függvényeket. (mert ugye nem csak a Te függvényeid fognak lefutni, hisz, ha felteszel pl. egy spamszűrőt, akkor az jól bele is alterelhet az űrlapodba)

    pp

    1
    0
    pero képe

    Akkor én már tényleg nem értem.

  • "A" user kilistázza az ajánlatokat, mondjuk hogy 3 ajánlat van. Ez 3 form egy oldalon
  • "B" user megteszi ugyanezt, szintén ott lesz az oldalán a 3 form
  • "A" user elvállalja az egyiket. így már csak 2 form lesz a oldalán
  • "B" usernek meg mindig 3 form van az oldalán, azaz az a form is ott van aminek már nem kéne.
  • "B" user el akarja vállalja azt az ajánlatot, amit már elvállaltak, de az Ő oldalán még ott volt az ajánlathoz tartozó form.
  • Nem értem hogy az ebből a HTML kódban létező formból mér nem kapok adatot. A böngésző gondolom elküldi a POST adatokat. De akkor hol tűnik el?

    Ugyanis a feldolgozás menete szerint szerintem:
    "Jött-e adat?" -> IGEN
    "Valid-e?" -> passz hiszen ide el sem jutok

    Vagy a form elküldése után először újragenerálja a drupal az egész oldalt, és mivel újrageneráláskor az a Form eltűnik, aminek az adatait a böngésző elküldte, így figyelmen kívül hagyja azokat?
    Ha így van, nincs valami hook, amiben mégis meg lehet azt nézni, hogy van-e elküldött "érvénytelen" form?

    Elnézést, hogy ha lassú a felfogásom :)
    Úgy érzem, hogy amíg ezt nem értem meg nem kéne továbbhaladnom, mert itt valami koncepcionális hiba van a gondolataimban :)

    0
    0
    pp képe

    Akkor még részletesebben:

    Ugorjunk az utolsó előtti lépéshez:

    "B" usernek meg mindig 3 form van az oldalán, azaz az a form is ott van aminek már nem kéne.

    Na itt megnyomja az elvállalom gombot a felhasználó, aminek hatására egy Post kérés küld a böngésző a szerver felé.

    Ok, és most csukd be a szemed és képzeld el, hogy mindent elfelejtettél ami eddig volt, hisz az esetünkben is pont ez van, alapból a szerver nem tudja, hogy B küldte a kérést, és azt se, hogy egy összeállított űrlapból jön a kérés és nem egy robot tolja azt.

    Emlékezet nélkül vagyunk!

    A szerver kap egy Post kérést. Van hozzácsapva egy sütemény, ami tartalmazza a session id-t, Hurrá, betölti a session-ben tárolt adatokat.

    Megnézi, hogy van-e q változó a GET tömbben, és megállapítja, hogy melyik függvényt kell meghívnia. (ezt szokták routingnak hívni, amikor egy adott útvonalhoz a keretrendszer meghatározza a végrehajtandó fv-t. Ezt kb. minden keretrendszerben így van, nem csak a Drupalban, sőt, a nem PHP-s keretrendszerekben is hasonló elven működik az útválasztás)

    Ezen a ponton még nem tudja a szerver, hogy kell-e valamit kezdenie a POST adatokkal.

    Meghívódik a formokat legeneráló kód.

    Ez a kód szintén nem foglalkozik a POST-ban található adatokkal. (még mindig nem foglalkozott vele senki!!! Felháborító!!! :D minek küldözgetjük mi ezeket az adatokat, mit gondolnak ezek??? :D )

    A kód kirakja az űrlapot. Na ez a kód megnézi, hogy jött-e adat. Látja, hogy a form_id az nem az ami az ő form_id-je. Nem foglalkozik vele. (még ő sem, hát akkor ki fog??? lusta egy banda ez vazzz!!! :D)

    Mondjuk, jó hülyén is nézne ki, ha foglalkoznának a másiknak küldött adatokkal, mert ha kint van a keresés és a bejelentkezés űrlap, akkor nem tudnál bejelentkezni, mert a kereső űrlap kódja, folyton rákeresne a nevedre, ahelyett, hogy beléptetne. Ezért véletlenül se foglalkoznak egymás adataival.

    Mivel, ha már elfogadtak egy ajánlatot, nem fogod kirakni az űrlapot, ezért senki nem fog a kéréseddel foglalkozni. (hálátlan egy banda na... :D)

    Mivel a feldolgozás és az űrlap kirakását is a drupal_get_form végzi, ezért, ha nem rakod ki az űrlapot, akkor nem fogja a Drupal feldolgozni a kérést sem.

    Szóval a lényeg, hogy a kommunikáció ezen formája (folyton a kliens kérdez a szervertől) emlékezet nélküli, szóval valahonnan ki kell találni mindig, hogy mi az amire a kliens mögött lévő felhasználó gondolt, amikor összeállította a böngésző és elküldte a kérést. Nincs űrlap, csak egy kérés, amiben a POST tömbben található adatok lehet, hogy egy böngészőben lerenderelt űrlapból érkeznek, de lehet, hogy nem.

    pp

    3
    0
    pero képe

    Köszönöm a részletes választ és legfőképpen a türelmed :)

    0
    0
    pero képe

    Arra jutottam elméletben, hogy az ajánlatok listázási oldalán nem lesz egy form se. Hanem 'Elvállalom' linkek egy 'page callback'-re ami megkapja paraméterben az ajánlat azonosítóját. Itt meg tudom vizsgálni hogy már nem vállalták-e el. Ha még nem akkor kiírom a form-ot a módosítható date mezővel és a submit gombbal. Mivel ez a form nem fog sosem "eltűnni" így be fogok jutni a form_validate-be is és tudok újra ellenőrizni...
    Remélem gyakorlatban is működni fog. :)

    Köszönöm a segítségeket :)

    0
    0
    pp képe

    Mindenképpen legyen token a linkek végén, mert különben CSRF támadással el lehet fogadtatni ajánlatokat mással. (biztonság)

    Ha regisztrációhoz se kötöd a link megjelenését, akkor pedig a keresők fogják "beindexelni" vagyis elvállalni az összes munkát. :) (koncepcionális probléma)

    0
    0
    pero képe

    Elvileg a linkre kattintva még nincs elvállalás, hanem a link utáni form elküldése után lenne.
    Az ajánlat elfogadása pedig regisztrációhoz kötött, sőt egy szerepkörhöz tartozáshoz is.
    Így gondolom nem szükséges Token.
    Bár ettől még utánanézek ennek, máskor jól jöhet.:)

    Gondolom ezekre a függvényekre gondolsz:
    drupal_get_token
    drupal_valid_token

    0
    0
    pp képe

    Jól gondolod.

    Igen ha csak egy akciót indítasz el, de nem maga az akció a link megnyomása, akkor nem kell token.

    pp

    0
    0