Varnish: érd el a maximális oldalbetöltési sebességet!

Mielőtt belefognánk…

A webről érkező kérések kezelése (különösen, ha dinamikusak) a webszervereken (pl. Apache) a rendszer-erőforrásokat igencsak igénybe veheti, emiatt egyre többen kezdték használni a könnyűsúlyú webszervereket (pl. ngix, nginx vagy lighttpd). Ezek az Apache rendszerrel összekötve statikus tartalomnál frontendként, vagy önállóan is működhetnek.

Azonban a weboldalad vagy webshopod javítására létezik egy másik módszer is: használhatsz webszerever-gyorsítót, mint a Varnish Cache, melyet a Varnish Software fejlesztett.

A Varnish Cache folyamatainak ismerete többféleképpen is elsajátítható: Varnish Book olvasása, Hivatalos dokumentáció, sok gyakorlás és kísérletezés. Különösen ajánlott az első kettővel kezdeni. Ennek a cikknek az anyaga mindegyik forrásból merít: pl. stackoverflow-n és más szakmai oldalakon található anyagokból, valamint személyes tapasztalatból is.

 

Mi a Varnish?

 

A VARNISH: egy nyílt forráskódú, fordított HTTP proxy cachelési szoftver, olykor webapplikáció-gyorsítóként is említik. A fordított proxy egy olyan proxy szerver, amely a látogatók számára egy szokásos szervernek tűnik. A memóriában fájlokat vagy részállományokat tárol (cache-el), melynek köszönhetően lecsökken a válaszidő és a hálózati sávszélesség-használat.

A Varnish Software szakmai támogatást is nyújt a Varnish-t használatához, amit egyébként egy kétzáradékos BSD-licensz szabályoz. A Varnish 1.0 2006-ban, a Varnish 2.0 2008-ban, a Varnish 3.0 2011-ben, a Varnish 4.0 pedig 2014-ben jelent meg.

A Varnis igen rugalmas, hiszen saját magad tudod hozzá megírni a cache-elési eljárást a Varnish Konfigurációs Nyelven (Varnish Configuration Language /VCL/), ami egy C nyelven alapuló alkalmazásspecifikus nyelv. Ez később aztán C kóddá fordul kompilálás előtt.

 

Hogyan működik a Varnish?

 

Tegyük fel, hogy a látogató egy kérést küld a szerveredre. Ezt először a Varnish kapja meg és a gyorsítótárban keres rá választ: ha talál érvényes tartalmat, akkor elküldi válaszként, ha nem, akkor továbbítja a kérést a backend felé. A backend válaszát a gyorsítótárba helyezi, aztán elküldi a látogatónak. Így ha legközelebb ugyanaz a kérés érkezik, már lesz kész válasz a gyorsítótárban.

Ezt a folyamatot egy egyszerű ábrán is szemléltethetjük:

 

varnish cache folyamat

 

A Varnish telepítése és alap konfigurálása

 

A Varnish legkönnyebben a szervered csomagkezelőjével tudod telepíteni, de ott lehet, hogy nem a legfrissebb verziója lesz elérhető. Ha a legfrissebb verziót szeretnéd, ahhoz is könnyen találhatsz megfelelő bináris csomagokat vagy forgathatsz egyet forráskódból; a hivatalos oldalról letölthető az aktuális verzió. Az egyszerűség kedvéért a példáink csomagkezelő használatát feltételezik, például a Debian 8 („Jessie”) rendszereken elég begépelni ezt:

 

[code]

apt-get install apt-transport-https
curl https://repo.varnish-cache.org/GPG-key.txt | apt-key add -
echo "deb https://repo.varnish-cache.org/debian/ jessie varnish-4.1" >> /etc/apt/sources.list.d/varnish-cache.list
apt-get update
apt-get install varnish

[/code]

Ha forrásból állítod össze, ellenőrizd, hogy a függőségek már jelen legyenek és nézd át a hivatalos dokumentációt is.

A következő lépés a konfiguráció. Először nézzük a konfigurációs fájlt:

/etc/default/varnish (Debian)

/etc/sysconfig/varnish (Red Hat)

 

Jó néhány hasznos és fontos beállítást találunk itt a Varnish daemonra vonatkozóan, mint pl. zárolt megosztott memória, alap TTL, szálak száma stb.

Nézzünk egy egyszerű példát:

[code]

NFILES=131072
MEMLOCK=82000
NPROCS="unlimited"
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=6081
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_MIN_THREADS=10
VARNISH_MAX_THREADS=50
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_SIZE=128M
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}"
VARNISH_TTL=120
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
             -f ${VARNISH_VCL_CONF} \
             -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
             -t ${VARNISH_TTL} \
             -p esi_syntax=0x2 \
             -p cli_buffer=16384 \
             -p http_resp_hdr_len=131072 \
             -p thread_pools=4 \
             -p thread_pool_add_delay=2 \
             -p thread_pool_min=15 \
             -p thread_pool_max=50 \
             -p thread_pool_timeout=3600 \
             -u varnish -g varnish \
             -S ${VARNISH_SECRET_FILE} \
             -s ${VARNISH_STORAGE}"

[/code]

Ezek a beállítások a Varnish háttérfolyamatára vonatkoznak, a cache-elés folyamatát és a kérések kezelését a VARNISH_VCL_CONF-ban meghatározott .vcl fájl írja le. A cache kezelést egy C-szerű nyelven, a VCL-en írhatjuk meg; ez C-re fordítható és bináris kóddá áll össze; akkor aktiválódik, amikor a kérések beérkeznek. Ennek köszönhetően a Varnish nem csupán cache-elési szempontból tekinthető egy erős HTTP processzornak, használhatjuk még terheléskiegyenlítésre, vagy akár egyszerű tűzfalnak is. A következőkben ezt részletesebben átvesszük.

 

Nézzük meg közelebbről!

 

Vegyük át részletesebben, hogyan működik a kérés feldolgozása a Varnish v4-ben.

Legelőször vessünk egy pillantást a Varnish Finite State Machine ábrájára, amely szemlélteti a folyamatokat:

 

varnish folyamatok

VCL_RECV

Ez a folyamat legeleje, mely közvetlenül a kérés fogadása és elemzése után kerül meghívásra. Meghatározza a további lépéseket a bejövő kérésekkel kapcsolatban: megváltoznak-e, cache-elve lesznek-e vagy sem, lesz-e valami továbbítva a backend-nek feldolgozásra vagy csak egy választ küldünk a gyorsítótárból, stb.?

 

VCL_HASH

A vcl_recv után hívódik meg. A kéréshez egy hash értéket hoz létre, ami a gyórsítótárban tárolt Varnish objektum azonosító kulcsa (lesz); majd a vcl_recv eredményétől függően más függvények felé továbbítja a kérést.

 

VCL_HIT

Akkor kerül meghívásra, ha a gyorsítótárban van a vcl_hash-ben generált azonosítónak megfelelő tartalom. Eredménye meghatározza, hogyan kezeljük a kérés további részét.

 

VCL_MISS

Akkor kerül meghívásra, ha a gyorsítótárban nincs megfelelő tatalom vagy ha a vcl_hit visszautasította/küldte a betöltést.

 

VCL_PASS

A pass mode-ba való belépéskor kerül meghívásra. Ebben a módban a kérés a backend felé továbbítódik, és a backend válasza a klienshez érkezik, de nem kerül bele a cache-be.

 

VCL_PIPE

A pipe mode-ba való belépéskor kerül meghívásra. Ebben a módban a kérés a backendhez kerül, és minden további adat, akár a klienstől, akár a backendtől származik, változatlanul továbbításra kerül, amíg a kapcsolat egyik vége be nem zárul.

 

 

Backend szerverek

A fenti leírásokban számos alkalommal esett szó a backend-ről. A Varnish szaknyelvében a „backend” bármilyen szervert jelenthet, amely a Varnish számára cache-elhető tartalmat nyújt és amelyet a Varnish felgyorsít.

Egy backend kiszolálgó elérését a .vcl fájlban deifniálhatjuk:

 

[code]

backend default {
        .host = "127.0.0.1";
        .port = "8080";
}

[/code]

 

A való életben találkozhatunk szerverösszeomlásokkal és egyéb backend problémákkal. A folyamatos működés biztosításához, meghatározhatunk egy paramétercsoportot (probe néven), amely a backend állapotát ellenőrzi.

[code]

backend default{
        .host = "127.0.0.1";
        .port = "8000";
        .probe = {
                .url = "/alive/";
                .timeout = 2s;
                .interval = 3s;
                .window = 10;
                .threshold = 9;
        }
}

[/code]

 

Ha ezt a paramétercsoportot beállítjuk, akkor a Varnish egy egyszerű GET kérést fog küldeni minden 3. másodpercben az „/alive/” url-nek, 2 másodperces időtúllépési limittel. Mivel az „ablakot” 10-re, a „küszöböt” pedig 9-re állítjuk, a backend egészségesnek minősül, amennyiben az utolsó 10 kérésből legalább 9 sikeresnek bizonyult. A Varnish nem küld adatokat olyan hosztoknak, amelyek nem megfelelő állapotúak.

Így amikor a Varnish-nak tartalomra van szüksége – az alap backend kapcsolatnak köszönhetően – tudja majd, hol találja. De mi a helyzet akkor, ha több backend szerverrel rendelkezünk vagy külön módszerünk van bizonyos kérések kezelésére? Új backendet-et határozhatunk meg (magic néven) és új szabályt adhatunk meg a kérések feldolgozásához:

 

[code]

backend magic {
        .host = "127.0.0.1";
        .port = "8000";
}
...
sub vcl_recv {
        if (req.url ~ "^/magic/") {
                set req.backend_hint = magic;
        } else {
                set req.backend_hint = default;
        }
}

[/code]

 

Szabadon megválasztható, hogy milyen módszerrel határozzuk meg egy kéréshez a backendet – szabályok mentén, paraméterek mentén vagy véletlenszerűen. Ezenkívül több backend szerver is csoportosítható egy „director”-ban. Ehhez be kell tölteni egy VMOD-ot (egy Varnish modult), majd meghívni bizonyos műveleteket a vcl_init-ben.

 

[code]

import directors;    # load the directors
 
backend default {
        .host = "127.0.0.1";
        .port = "8080";
}
backend magic {
        .host = "127.0.0.1";
        .port = "8000";
}
 
sub vcl_init {
        new foobar = directors. round_robin();
        foobar.add_backend(default);
        foobar.add_backend(magic);
}
 
sub vcl_recv {
        set req.backend_hint = foobar.backend();
}

[/code]

Ez a módszer segít, hogy a Varnish-t terhelés-optimalizálásra használhassuk. Itt egy „round-robin director”-t használunk, de létezik egy „random director”, amely véletlenszerűen osztja el a kéréseket.

 

Edge Side Includes (ESI)

Olykor szükség van rá, hogy egyedi információt jelenítsünk meg minden egyes felhasználónak, pl. felhasználónév, kosár tartalma, korábban rendelt termékek listája stb. Ezeknek az adatoknak a cache-elése komoly problémát okozhat, ezért a Varnish-ban alkalmazott ESI blokkok különösen hasznosak.

Az ESI használatával az oldalunkat kisebb részekre oszthatjuk és eldönthetjük, ezekből melyik legyen cache-elve és melyik ne, majd ezekből állíthatjuk össze a választ. Ezeknek a részeknek külön cache beállításai lehetnek. Például, ha van egy listád a webáruházadban lévő 5 legnépszerűbb termékkel, ezt a listát valószínűleg elég egyszer cache-elni és így szerepeltetni más oldalakon, míg más részek esetleg soha nem kerülnek cache-elésre és mindig dinamikusan kell betölteni azokat.

 

varnish esi diagram

 

Ezzel a stratégiával növelheted a találati arányodat és csökkentheted a szerverek terhelését.

Az ESI csupán egy kis részét használja a Varnish teljes funkcionalitásának, ám ez is elegendő. Három ESI utasítást (statements) használhatunk:

  1. esi:include
  2. esi:remove
  3. <!–esi …–>

A Varnish nem dolgozza fel az ESI instrukciókat HTML kommentekben (comments).

Lássunk néhány példát.

 

ESI:include

Az alábbiakban egy egyszerű példát írunk le, ez a user.php fájl:

<?php
…
// Including some libraries
..
$user = $this->getUser();
echo $user->getName();
?>

 

 

Most nézzünk egy HTML fájlt, mely egy ESI include statement-tel rendelkezik. Ez a test.html:

<HTML>
    <BODY>
        Hello <esi:include src="user.php"/>!
    </BODY>
</HTML>

 

Az utolsó lépés az ESI élesítésére az ESI feldolgozás aktiválása VCL-ben.

[code]

sub vcl_backend_response {
        if (bereq.url == "/test.html") {
               set beresp.do_esi = true;
               set beresp.ttl = 24 h;   
        } elseif (bereq.url == "/user.php") {
               set beresp.ttl = 1m;
        }
}

[/code]

 

 

ESI:remove and <!–esi … –>

 

Mit csináljunk, ha az ESI nem áll rendelkezésre, mert nincs mondjuk engedélyezve? Erre az esetre ott vannak az <esi:remove> és <!–esi … –> konstrukciós műveletek. Ezeket bármikor lehet használni tartalmak megjelenítésére, akár él az ESI, akár nem. Például megadhatsz konkrét tartalmat akkor, ha az ESI rendelkezésre áll, és linkelhetsz rá, ha nem.

A “<!–esi” és az ending “-→” tag-eket az ESI feldolgozók eltávolítják az oldal feldolgozásakor, de a köztük levő tartalmat nem. A szintaxis miatt a feldolgozatlan esi tag-ek HTML/XML kommentként működnek.Az <esi:remove> tag-eket az ESI processzorok eltávolítják minden köztük lévő lévő tartalommal együtt.

Például:

<esi:remove>
  <a href="http://www.example.com/LICENSE">The license</a>
</esi:remove>
<!--esi
<p>The full text of the license:</p>
<esi:include src="https://example.com/LICENSE" />
-->

 

Ahogy a péládból is látszik, ezekkel a tagekkel megoldható, hogy dinamikus blokk tartalma jelenjen meg amikor van ESI feldolgozás és egy egyszerű link amikor az ki van kapcsolva.

 

Kik használják a Varnish cache -t?

 

A Wikipédia szerint a világ 10 ezer leglátogatottabb oldalának mintegy 10%-a használja a Varnish-t – hírportálok, közösségi oldalak, médiaoldalak stb. Néhány fontosabb ezek közül: Wikipédia, Facebook, Twitter, Vimeo, The New York Times, The Guardian, The Hindu, Tumblr.

 

A Varnish használata a Magento-nál

 

A Magento a világ legnépszerűbb e-kereskedelmi platformja. Híres rugalmasságáról, de a gyorsaságáról már kevésbé. Ha a Varnish-t Magento 1-hez szeretnéd használni, keress egy külső modult, vagy, ha időd engedi: csináld magad. Ez esetben meg kell tanulnod a Magento használatát, értened kell a HTTP Headerekhez, valamint a fordított proxy cache-eléshez és a Varnish Cache-hez is.

"Csupán két nehéz dolog van a számítástechnikában: a cache érvénytelenítés és az elnevezés."Phil Karlton

A Magento 2 esetében könnyebb a helyzet. A Magento 2 hivatalosan is támogatja a Varnish 3.0.5 és frissebb verzióit és bármely 4.x verziót. Elég, ha követed az útmutató dokumentáció lépéseit, de itt most össze is foglaljuk neked a főbb pontokat:

  • Telepítsd a Varnish-t és teszteld egy tetszőleges Magento oldal megnyitásával, hogy lásd, kapsz-e a válasszal egy HTTP header-t, amely jelzi, hogy a Varnish működik.
  • Telepítsd a Magento szoftvert és a Magento adminisztrációs felületén hozz létre egy Varnish konfigurációs fájlt.
  • Cseréld le a már létező Varnish konfigurációs fájlt az adminban létrehozott fájlra.
  • Tesztelj ismét mindent, és ha semmit sem látsz a /var/page_cache könyvtárban, sikeresen konfiguráltad a Varnish-t a Magento-ban!

Végül érdemes megjegyezni, hogy a saját (vagy ügyfeleidé, ha másoknak fejlesztesz) webáruháza legyen az utolsó hely, ahol az új VCL/PHP kódokat teszteled!

 

 

Drupal Commerce vagy Magento: melyik jobb a vállalkozásod számára?

Az alábbiakban részletesen végigvesszük a két rendszer közötti eltéréseket: milyen funkciókkal rendelkeznek, hogyan használhatóak, mennyire fejleszthetőek és kinek ideálisak?

  • Az alapvető különbségek: tartalom kontra e-kereskedelem
  • Drupal Commerce és ÜberCart
    • Különbségek
    • Amiben hasonlítanak
  • Magento és Drupal: a konkrét különbségek
    • Megjelenés, dizájn
    • Telepítés
    • Adminisztrációs felület
    • Funkciók
    • SEO
    • Skálázás
    • Fejlesztői közösség
  • Melyiket használják a webáruházak?
  • Összegzés – a végső ítélet

 

Mindenekelőtt érdemes tisztáznunk, alapjaiban mennyiben tér el a Drupal és a Magento. A Drupal rendeltetése szerint egy tartalomkezelő rendszer, (Content Management System, vagyis CMS), míg a Magento egy kifejezetten e-kereskedelmi (ecommerce) feladatokra fejlesztett webáruház motor. Mindkét rendszer olyan alapfunkcionalitással bír, amely lehetővé teszi, hogy e szerepükben helytálljanak.

 

Az alapvető különbségek: tartalom kontra e-kereskedelem

 

A Drupal rendszert elsősorban valóban tartalmak közlésére és menedzselésére tervezték : nagyszerűen építhetőek fel rajta blogok, tájékoztató oldalak. Ha egy weboldalon különféle tartalmakat akarunk kínálni, így híreket, e-könyveket, fórumot, közösségi tartalmakat, akkor erre a Drupal remek lehetőségeket kínál. Ahhoz, hogy e-kereskedelmi funkcionalitással is felruházhassuk, a Drupal Commerce-t kell használnunk, a fókusz azonban itt elsősorban a nem hagyományos típusú termékek értékesítésére helyeződik.

Drupal webáruház

Ez alatt azt kell érteni, hogy ha például előfizetős tartalomszolgáltatást akarunk nyújtani, esetleg olyan rendszert kiépíteni, amelyben a különféle előfizetői szinteknek, csomagoknak megfelelően máshoz férnek hozzá a felhasználók, akkor a Drupal maradéktalanul kiszolgálja igényeinket.

Hagyományosabb típusú termékkínálat értékesítésére azonban kevésbé alkalmas a Drupal Commerce, mivel ehhez jó eséllyel komplexebb kategóriarendszerre, illetve számos különféle jellemzővel felruházható terméktörzsre lesz szükségünk.

A Magento motort ellenben kifejezetten a hagyományosabb típusú termékek online értékesítése céljából fejlesztették ki: olyan kis- és nagykereskedelmi célokra egyaránt alkalmazható webáruházat kapunk általa, amely igény szerint természetesen a digitális termékek kereskedelmét is támogatja.

Magento webáruház

A Magento épp azon a téren van hátrányban, ahol a Drupal erősebb: a tartalomszolgáltatásban nem túl erős, kevés opciót kínál.

Ha valaki olyan e-kereskedelmi oldalt szeretne felépíteni, amelyen a tartalomszolgáltatást és az online kereskedelmet egy helyen végezheti, e célra talán a WordPress rendszere a legalkalmasabb, amolyan kompromisszumos megoldásként. A WordPress kezelésének elsajátítása ugyanis meglehetősen egyszerű, a tartalomkezelésben igen erősnek mondható, jól keresőoptimalizálható, a közösségi média kampányokban könnyen integrálható, emellett pedig a WooCommerce révén megfelelő alapfunkcionalitással bíró webáruház modullal is bővíthető (bár ez természetesen egy egyszerűbb megoldás, mint a Magento esetében).

 

Drupal Commerce és ÜberCart

 

A Drupal alapjaira már kétféle e-kereskedelmi modult is választhatunk Ezek közül a régebbi az ÜberCart, az újabb pedig a Drupal Commerce névre hallgat.

 

Az ÜberCart még a legelső ilyen modul volt, amelyet a Drupal 6-hoz készítettek, az évek során természetesen sokat fejlődött funkcionalitásában, működésében, összességében sokkal felhasználóbarátabb lett. A tapasztalatok szerint gond nélkül épül be a Drupal oldalakba, beépített fizetéskezelési (payment processing) lehetőséggel is rendelkezik, a webáruház pedig könnyen telepíthető a segítségével.

 

Különbségek

 

Az ÜberCart egyik gyengesége, hogy content node-ként kezel minden egyes terméket, vagyis minden egyes információ, minden jellemző az adott termékhez kötött, ami okozhat néha hibákat a szállításnál, számlázásnál. Éppen ezért olyan termékek esetében egyszerűbb használni, amelyeknél nem kell variációkat megadni: ilyenek lehetnek például a könyvek. Amennyiben például egy nadrágot akarunk bevinni, amely több méretben és színben is kapható, úgy a „nadrág” node alá kerülnek be a különféle variációk, a rendszer felépítése miatt pedig ez nagyszámú terméknél már zavaró lehet.

A Drupal Commerce esetében ellenben minden egyes termékvariáció önálló entitásként van jelen, nem pedig az eredeti termék elágazásaiként. Ahhoz, hogy a különféle változatokat egy helyen jelenítsük meg (tehát az adott nadrágtípus oldalán az összes színt és méretét például) létre kell hoznunk egy külön csomópontot (ez lesz a display node). Az ÜberCartnál bonyolultabb alapokon nyugvó rendszer ez, azonban elméletben közelebb áll a hagyományos terméknyilvántartásokhoz.

A Drupal Commerce-nek nem része a fizetési rendszer, ezen funkciók használatához további modulokat kell telepítenünk hozzá – az ÜberCart esetében ugyanakkor a fizetés és szállítás modulok is opcionálisan alapból telepíthetőek.

 

Amiben hasonlítanak

 

Mind a két változat telepítése viszonylag egyszerű, az alapbeállítások használatával egy jól funkcionáló webshopot kapunk. Az adminisztráció összességében nem tér el nagyban, legalábbis a kezelőfelületet tekintve, illetve a webboltok teljes mértékben testreszabhatóak. A Drupal Commerce kezelésének elsajátítása ugyan tovább tart, de az ÜberCart és az újabb Drupal kereskedelmi modul egyaránt viszonylag egyszerűen kitanulható.

 

Magento és Drupal: a konkrét különbségek

 

Az egyszerűség kedvéért a következőkben a két Drupal modul közül az újabb, többek által használt változat, a Drupal Commerce képességeit hasonlítjuk össze a Magento CE, vagyis a Magento rendszer ingyenes, nyílt forráskódú változatának képességeivel.

Drupal Commerce funkcionalitás

Telepítés

 

Az első jelentősebb különbség már a telepítésnél megmutatkozik. A Magento telepítése rendkívül egyszerű folyamat, körülbelül tíz perc alatt elvégezhető – a Magento esetében komplexitással a későbbiekben találkozunk csak, egy webáruház létrehozása szinte már játszi könnyedséggel megy.

A Drupalnál viszont az alapfokú ismeretekkel rendelkező felhasználó már belefuthat problémákba. A hivatalos oldalon egy külön Commerce Kickstart applikációt kínálnak, amely lehetővé teszi, hogy „kihagyjunk több hétnyi konfigurációt, amíg a Drupal Commerce keretrendszerben felépítjük ecommerce oldalunkat”.

A Kickstart viszonylag gyorsan végez, így elméletileg egy teljesen működőképes próbaboltot (demo store) kapunk témával, katalógussal, kereséssel és back office interfésszel, viszont értelemszerűen a finomhangolás nélkül – ha viszont első webáruházat építünk, akkor ez nem jelent nagy problémát.

 

Adminisztrációs felület

 

A Magento interfésze egy jól kiforrott, sokat próbált felület, amelyet alapvetően igen könnyű kiismerni, ami magát a kezelést illeti. Az olyan felhasználók számára azonban, akik a Magento használatában kezdők, zavaró lehet a rengeteg különféle opció: nem könnyű eligazodni a rengeteg menü, opció, beállítási lehetőség között, mindenképpen időt igényel a tanulás.

Online viszont számos tréningvideó, leírás, kurzus érhető el, amely segíthet abban, hogy az alapok használatát ösztönössé tegyük – a későbbiek során pedig elsajátíthatjuk, hogyan működnek a rendszer azon részei, amelyek már a komplexebb beállításokat rejtik.

A Drupal esetében sem öt percbe telik megtanulni, pontosan mi hogyan működik és hol is találjuk a fontos dolgokat, ugyanakkor a tartalomkezelő rendszer újabb verziói már jóval könnyebben kezelhetőek elődjeiknél. Előnyt jelenthet a kezelésben az is, hogy az adminisztrációs modult megjeleníthetjük a weboldalon is, így gyorsabban módosíthatunk bizonyos dolgokat.

 

Funkciók

 

Mint eleve webáruháznak tervezett rendszer, a Magento kínál olyan plusz funkciókat, amelyeket a Drupal esetén hiába keresnénk. Ezek közül néhányat az alábbiakban részletezünk.

A Magento nagy erőssége más e-kereskedelmi platformokkal szemben, hogy lehetőség van a szűkítő keresésre, ami a felhasználóknak kényelmes és hatékony módot kíván arra, hogy az igényeiknek leginkább megfelelő terméket megtalálják akár több ezer termék között is.

A szűkítő kereséssel kereshetünk kategóriák, árkategória, brand, szín vagy bármely más változó alapján. Ez azért is különösen fontos, mert a Magento egyedülálló lehetőségeket ad arra, hogy a termékek jellemzőit testre szabjuk az adatbázisban, gyakorlatilag bármilyen változót megadhatunk, így a webáruházat akkor használhatják igazán kényelmesen a felhasználók, ha lehetőségük van mindenre rákeresni. Ez tehát szintén a nagy darabszámmal és változatos termékekkel működő webáruházak számára biztosít előnyt.

A Magento rengeteg beépített promóciós lehetőséget is kínál, amelyek ideálisak arra, hogy a látogatókat konvertáljuk, hogy az átlagos vásárlási értéket növeljük, csökkentsük a kosárelhagyók arányát vagy éppen újra aktiváljuk régebbi vásárlóinkat.

magento CMS-t használó brandek

Kuponos akciókat indíthatunk, egyedi kedvezményeket kínálhatunk bizonyos termékcsoportokra vagy akár felhasználók adott csoportjainak (például a visszatérő vásárlóknak kedvezményt adhatunk), lehetőségünk van cross- és upsell akciókra és így tovább.

Termékajánló modult is beépíthetünk, hogy ez utóbbi lehetőségeket a legjobban ki tudjuk használni. Elhelyezhetünk CTA-kat, amelyek a vásárlás egy adott pontján arra ösztönzik a vásárlót, hogy az adott termékből többet/nagyobbat rendeljen (pl. takarítson meg azzal, hogy egy termékből előre megrendeli a szükséges mennyiséget, akár kedvezményt is adhatunk erre), vagy hogy egy olyan másik termékkel együtt tegye a kosárba, amelyet mások gyakran párosítanak hozzá (pl. egy laptophoz plusz tárhelyet ajánlhatunk).

A Community Edition rendszer hűségrendszert, hűségpont-kezelést alapvetően nem tartalmaz, vásárolt vagy fejlesztett modulként azonban bővíthetjük ilyen funkcionalitással is a webáruházat. Összetett kampányokat futtathatunk akár e-maileket is küldve, felhasználói csoportokat alakítva ki, adott az eszközkészlet arra, hogy egy felhasználó vásárlóélettartam-értékét a lehető legnagyobbra növeljük.

 

Megjelenés, dizájn

 

A Drupal igen gazdag testreszabási lehetőségekkel rendelkezik, ami a megjelenését illeti, és a rendelkezésre álló témákon belül is sok mindent módosíthatunk, viszonylag könnyen teljesen egyedivé tehetjük az oldalunkat.

drupal commerce áruházak

A Magento esetében is rendelkezésre állnak ingyenes és fizetős dizájnok is, a legjobban viszont természetesen akkor járunk, ha teljesen saját megjelenést adunk a webboltnak. Ebben az esetben, ha kicsit nagyobb költségvetéssel rendelkezünk, akkor igazán egyedi és különleges megjelenítést is létrehozhatunk a megfelelő szakemberek segítségével. A Magento rendszere azonban igen bonyolult és kényes, így nem ajánlott, hogy amatőrként bárki elkezdjen kísérletezgetni komolyabb átszabásával.

 

SEO

 

Alapvetően mind a két rendszer jól keresőoptimalizálható, e téren azonban a Drupalnak előnye van tekintettel arra, hogy eleve tartalomkezelő rendszerként működik. Ennek ellenére a Magento esetében sem kell lemondanunk arról, hogy jól rangsoroljunk a találati oldalakon, a rendszer összességében igen jól optimalizált, ugyanakkor egyes témák esetében a tapasztalatok szerint találkozhatunk nehézségekkel (ezért is lehet praktikusabb, ugyanakkor természetesen drágább és időigényesebb szakemberek segítségével saját fejlesztéseket használni).

 

Skálázás

 

Ebben a körben egyértelműen a Magentónál az előny. A sok terméket, illetve azok különféle változatait áruló webáruházak nem véletlenül épülnek inkább erre a rendszerre, és ritkán Drupalra. Ahogyan arról beszéltünk is, a Drupal erőssége a tartalom, és igazán akkor ideális, ha kisszámú és kevés különböző jellemzővel rendelkező terméket akarunk eladni, így könyveket például.

A Magento gyakorlatilag gond nélkül képes sok ezer terméket kezelni, rengeteg különféle (akár teljesen egyedi) attribútumot rendelhetünk ezekhez, amelyek könnyen kereshetőek is. Ha tehát nagyra törünk, és egy nagyvállalat egész termékskáláját akarjuk értékesíteni, vagy olyan áruházat építünk, ahol eleve nagyszámú terméket értékesítünk, akkor a Magento felé billen a mérleg nyelve.

 

Fejlesztői közösség

 

Mindkét rendszer mögött erős és aktív nemzetközi közösség áll, akik kreatív fejlesztéseket végeznek rajtuk. E fejlesztések iránya azonban alapvetően más: míg a Magento esetében az ecommerce funkcionalitás áll a középpontban, addig a Drupal rendszerrel foglalkozók jellemzően a CMS funkciókat bővítik, finomítják vagy gondolják újra – tehát az e-kereskedelmi modulokkal aránylag kevesen foglalkoznak.

 

Melyiket használják a webáruházak?

 

A Magento évek óta az egyik legnépszerűbb webáruházmotor, részesedését az e-kereskedelemben a felmérések szerint csak a WordPress rendszerű WooCommerce képes felülmúlni. A nagyobb látogatottságú oldalak, illetve a nagyvállalatok leginkább a Magento rendszerét használják.

Világszinten a legfrissebb adatok szerint körülbelül az összes webáruház negyede használja a Magentót, a Drupal Commerce és ÜberCart részesedése ezzel szemben szinte mérhetetlen – a legtöbb felmérésben nem is szerepel, vagy az „egyéb” kategóriába sorolják.

 

ÖSSZEGZÉS

Ha olyan weboldalt akarunk építeni, ahol elsősorban a tartalommarketing eszközeivel népszerűsítjük kisebb számú termékünket, akkor a tartalomkezelő rendszerként működő Drupal jobb megoldás lehet. Ha pedig a tartalom másodlagos, így például egy blogot akarunk csak vezetni az oldalon, miközben komoly termékskálát kínálunk és komplexebb webáruház-funkcionalitásra is szükségünk van, akkor a Magento optimálisabb választás.

drupal nem főként e-kereskedelemre

Induló e-kereskedelmi vállalkozásokra a Drupal javasolt inkább, mert egyszerűbb és nem utolsósorban olcsóbb testre szabni, – bár fejlesztői tapasztalt nélkül szintén nem javasolt komolyabban átalakítani, a Magento esetében viszont biztosan fejlesztői segítségre lesz majd szükségünk.

Nagyvállalatok, a termékeiket netre költöztető komolyabb kereskedelmi láncok, tehát a komolyabb igényekkel rendelkezők viszont úgy tapasztalják majd, hogy megéri befektetni egy megfelelően optimalizált Magento oldal létrehozásába.

 

 

Hány látogatót tud kiszolgálni a webáruházad? Teszteld!

Mi is az a teljesítmény tesztelés?

Az ISTBQ (International Software Testing and Qualification Board) szerint a teljesítmény tesztelés nem más, mint a “Tesztelési folyamat, mellyel a szoftvertermék teljesítményét lehet meghatározni.”

A Wikipédia szerint: Tesztelési folyamat, amely meghatározza, hogy milyen gyors a rendszer egy bizonyos szempontból, adott terhelés alatt. Ezenkívül validálhat és verifikálhat más minőségi tényezőket is, mint skálázhatóságot, megbízhatóságot vagy forrás kihasználtságot.

Tehát egy olyan nem funkcionális tesztelési folyamat, amely az alkalmazás (szoftver, azaz Magento webáruház) teljesítményével foglalkozik és a fejlesztés teljes szakaszában használható a problémák felfedésére és megelőzésére.

Szerintem a Wikipédia megállapítása áll legközelebb a meghatározáshoz, azonban pontos definíciót adni, mint sok más esetben, úgy itt sem lehet.

 

Miért van szükséged teljesítmény tesztelésre?

Az internet térhódításával egyre többen élnek az online vásárlás lehetőségével, így egyre többen érik el a webáruházakat is. Manapság már az emberek többsége – ahelyett, hogy a boltba menne és kipróbálná a terméket az eladótól segítséget kérve – már szinte igazi szakértőként összehasonlít, vizsgál, keresgél a világ minden webáruházának termékeiből, hogy számára a legmegfelelőbbet találja meg. Egy ünnep vagy nagy akció (mint például a Karácsony vagy a Fekete Péntek) közeledtével pedig a boltok helyett a web világában rohanják meg a vásárlók áruházunkat.

Ezeket a megnövekedett terheléseket vagy a folyamatos, erős forgalmat áruházunknak ki kell szolgálnia, ezért is szükséges a teljesítmény tesztelése.

A webáruházat több komponens alkotja, van szoftveres, hardveres és hálózati keresztmetszet része is, melyek további szegmensekre bonthatók egészen a használt keretrendszer, a szerver processzorának teljesítménye vagy a letöltött oldal kódjának adat mennyiségéig. Ha ezekre nem figyelünk oda, akkor egy-egy kiugró terhelés esetén a webáruházunk lelassulhat, elérhetetlenné válhat vagy a legrosszabb esetben teljesen össze is omolhat.

 

A teljesítmény tesztelés fajtái

Az alábbi felsorolásban ismerjük meg a releváns teljesítmény tesztelés fajtáit, a teljesség igénye nélkül, kiemelve a webáruházakon alkalmazott legfontosabb teszteléseket. Részletesebb információért a Wikipédia szoftver tesztelés leírását tudom ajánlani.

1. Terheléses tesztelés (Load testing): A legegyszerűbb teszt, mivel itt azt verifikáljuk, hogy az alkalmazás hogyan viselkedik normális vagy magasabb terhelés alatt, változik-e a viselkedése mindeközben.

   a) Tűrőképesség teszt (Endurance testing): Hosszabb ideig tartó, folyamatos terhelés mellett vizsgálja a rendszer működését, így az egy-két órás tesztek alatt rejtve maradó problémákat az akár több napos teszt segíthet a felszínre hozni.

2. Stressz tesztelés (Stress testing): Ideális esetben arra használjuk, hogy megismerjük a rendszer felső határait, a töréspontokat. Ez a teszt típus kész meghatározni a rendszer robusztusságát extrém terhelés közben, és segíti az adminisztrátorokat, hogy meghatározzák a rendszer ideális működési tartományát és maximumát.

   a) Kapacitás teszt (Capacity testing): A Stressz tesztelés folyamán megállapítjuk, hogy mennyi felhasználót / lekérdezést / műveletet tud egyszerre végrehajtani hibamentesen a rendszer.

3. Kitartás tesztelés (Soak testing): A normális működést szimulálva a kitartás teszt egy olyan működés, ami képes meghatározni, hogy a folyamatos használati terhelését mennyire bírja a rendszer. A teszt alatt folyamatosan figyelni kell a memóriát és ezzel felderíthetők a memória szivárgási problémák.

4. Tüske tesztelés (Spike testing): Hirtelen megugrott terhelést adunk a rendszernek, majd ezt ugyanilyen hirtelen lecsökkentjük. Ez a gyors változtatás „tüskeként” jelenik meg a rendszer terhelésének grafikonjában, innen kapta a teszt a nevét.

5. Konfigurációs tesztelés (Configuration testing): Felmerülhet a kérdés, hogy ez a teszt mit keres a teljesítmény tesztek között. Mivel ez a teszt azt vizsgálja, hogy a konfigurációs beállítások megváltoztatásával milyen hatással van a rendszer részeire vagy egészére, elsősorban a teljesítményére, ezért az itteni említése jogos.

6. Izolációs tesztelés (Isolation testing): A teljesítmény tesztelésnek nem egy egyedi típusáról van szó, inkább egy ismétlődő tesztelésről, ami fényt derít egy rendszerproblémára. A tesztek gyakran külön környezetekbe izoláltak és a teszt eredményeként csak a hibás környezetet kapjuk eredményül.

teljesitmény teszt PET ábra

A teljesítmény tesztelési típusok fajtái

A tesztelés lépései

      1. Tesztkörnyezet meghatározása (létrehozása, kialakítása)
      2. Elfogadási kritérium(ok) meghatározása
      3. Teszt(ek) megtervezése (tesztelési forgatókönyv megírása)
      4. Tesztkörnyezet konfigurálása (adatfeltöltés, paraméterek beállítása)
      5. Tesztek implementálása
      6. Tesztek futtatás
      7. Eredmények kiértékelése, riportok készítése, újrafuttatás

Tsung teljesítmény teszt logó

 

Tsung (IDX-Tsunami 1.6.0)

A Tsung egy megosztott terheléses teszt eszköz. Protokoll függő és jelenleg a következő protokollal kommunikáló szervereken futtatható:

A Tsung fő erőssége, hogy képes szimulálni egyetlen gépről nagy mennyiségű felhasználót. Ha több gépen (klaszter) használjuk, akkor pedig igazán lenyűgöző teljesítményt tud produkálni, mindezt könnyen konfigurálhatjuk és fenntarthatjuk.

Tsung működése ábra, teljesítmény teszt

A Tsung működésének elvi vázlata

 

Jellemzők

  • Magas teljesítmény
  • Elosztott működés
  • Több protokoll támogatás
  • SSL támogatás
  • Különböző IP címek kiosztása azonos gépről
  • Operációs rendszer monitorozása a tesztelés alatt
  • XML konfigurációs rendszer
  • Dinamikus forgatókönyvek (tranzakciók)
  • Kevert viselkedés a felhasználóknak (munkamenetek)
  • Sztohasztikus feldolgozás (thinktime)

 

Mi az az Erlang és miért fontos?

A Tsung-ot Erlang nyelven fejlesztették és ez az, ami olyan erőssé teszi, mivel az Erlang egy párhuzamosság-orinetált programozási nyelv (concurrent programming). Az Erlang OTP (Open Transaction Platform) képezi a Tsung alapját, ami így a következő főbb jellemzőkkel ruházza fel:

      • Teljesítmény
      • Skálázhatóság
      • Hibatűrő képesség

 

Protokollok és teljesítmény

A Tsung nagy teljesítményre képes, ha a megfelelő hátteret biztosítjuk a számára. Számokban ez az alábbiakat jelenti:

      • Jabber/XMPP protokoll:
        • 90,000 szimulált JABBER felhasználó egy 4-es Tsung klaszteren.
        • 10,000 szimulált felhasználó: Tsung 3 számítógépből (800MHz-es CPU) álló klaszteren.

 

      • HTTP és HTTPS protokoll:
        • 12,000 szimulált felhasználó: Tsung 4 számítógépből álló klaszteren (2003) 3000 lekérés/másodperc
        • 10,000,000 szimulált felhasználó: Tsung 75 számítógépből álló klaszteren több, mint 1,000,000 lekérés/másodperc

 

A Tsung használata

 

A Tsung teljesítmény tesztelés végrehajtásához először is fel kell telepítenünk azt a szerverünkre, melyhez az Amazon EC2 – Virtual Server Hosting szolgáltatása az egyik legkézenfekvőbb és kényelmes szolgáltatás.

A Tsung telepítése

A Tsung telepítését kísérjük most végig lépésről lépésre, hogy mindenki számára könnyen érthető legyen. A VPS szerver konfiguráció, amire telepítésre kerül, a következő:

      • CentOS 6.7 opercáiós rendszer
      • CPU: 8 magos (Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz)
      • Memória: 3 GB
      • Tárhely: 10 GB HDD

 

      1. Első lépésként az Erlang-ot kell telepíteni, valamint a Firefox-ot is, mivel azon keresztül történik a jelentések generálása (és megnyitása, ha GUI-t használunk).

 


[root@aion-tsung ~]# yum -y install erlang perl perl-RRD-Simple.noarch perl-Log-Log4perl-RRDs.noarch gnuplot perl-Template-Toolkit firefox

 

      1. Ezt követően töltsük le a Tsung-ot és installáljuk:

 


[root@aion-tsung ~]# wget http://tsung.erlang-projects.org/dist/tsung-1.6.0.tar.gz
[root@aion-tsung ~]# tar zxfv tsung-1.6.0.tar.gz
[root@aion-tsung ~]# cd tsung-1.6.0
[root@aion-tsung ~]# ./configure && make && make install

 

      1. A Tsung jelentés elkészítésének parancsára hozzunk létre egy pre-set parancsot (alias command) a könnyebb használat érdekében (vim használatával):

 


[root@aion-tsung ~]# vim ~/.bashrc
vim > alias treport="/usr/lib/tsung/bin/tsung_stats.pl; firefox report.html"
vim > :w
vim > :q
[root@aion-tsung ~]# source ~/.bashrc

 

      1. Készítsük elő az első felhasználáshoz a Tsungot (opcionális):

 


[root@aion-tsung ~]# cd /root/
[root@aion-tsung ~]# mkdir .tsung
[root@aion-tsung ~]# cd ..
[root@aion-tsung ~]# cp /usr/share/doc/tsung/examples/http_simple.xml /root/.tsung/tsung.xml

 

A Tsung konfigurálása (/root/.tsung/tsung.xml)

A Tsung XML konfigurációs állományának első szintje meglehetősen kötött, azonban a munkamenetek és a tranzakciók használatával jól irányíthatóak a folyamatok és a monitorozás. Nézzük meg példának az examples/http_simple.xml állományt:

<?xml version="1.0"?>
<!DOCTYPE tsung SYSTEM "/usr/share/tsung/tsung-1.0.dtd">
<tsung loglevel="notice" version="1.0">

    <!-- Client side setup -->
    <clients>
        <client host="localhost" use_controller_vm="true"/>
    </clients>

    <!-- Server side setup -->
    <servers>
        <server host="195.56.150.103" port="80" type="tcp"></server>
    </servers>

    <!-- to start os monitoring (cpu, network, memory) -->
    <monitoring>
        <monitor host="195.56.150.103" type="snmp"></monitor>
    </monitoring>

    <load>
        <!-- several arrival phases can be set: for each phase, you can set
        the mean inter-arrival time between new clients and the phase
        duration -->
        <arrivalphase phase="1" duration="10" unit="minute">
            <users interarrival="2" unit="second"></users>
        </arrivalphase>
    </load>

    <options>
        <option type="ts_http" name="user_agent">
            <user_agent probability="80">Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20050513 Galeon/1.3.21</user_agent>
            <user_agent probability="20">Mozilla/5.0 (Windows; U; Windows NT 5.2; fr-FR; rv:1.7.8) Gecko/20050511 Firefox/1.0.4</user_agent>
        </option>
    </options>

    <!-- start a session for a http user. the probability is the
    frequency of this type os session. The sum of all session's
    probabilities must be 100 -->

    <sessions>
        <session name="http-example" probability="100" type="ts_http">

            <!-- full url with server name, this overrides the "server" config value -->
            <request>
                <http url="/" method="GET" version="1.1"></http>
            </request>
            <request>
                <http url="/images/accueil1.gif" method="GET" version="1.1" if_modified_since="Fri, 14 Nov 2003 02:43:31 GMT"></http>
            </request>
            <request>
                <http url="/images/accueil2.gif" method="GET" version="1.1" if_modified_since="Fri, 14 Nov 2003 02:43:31 GMT"></http>
            </request>
            <request>
                <http url="/images/accueil3.gif" method="GET" version="1.1" if_modified_since="Fri, 14 Nov 2003 02:43:31 GMT"></http>
            </request>

            <thinktime value="20" random="true"></thinktime>

            <request>
                <http url="/index.en.html" method="GET" version="1.1"></http>
            </request>

        </session>
    </sessions>
</tsung>

A fenti példában a szerverre telepített tsung a localhost-ról indítja a klienseket, melyek célja a 195.56.150.103 ip címen lévő szerver 80-as portja. A szerver monitorozására az SNMP protokollt használjuk és egy tsung klienst. A <load> szakaszban látható, hogy egy szakaszból áll a teszt, ami 10 percig fut és 2 másodpercenként hoz létre egy felhasználót. Az <options> szakaszban megadott böngészővel azonosítják magukat a létrehozott felhasználók 20-80%-os arányban.   A <sessions> munkameneteket leíró szakasz határozza meg a felhasználók interakcióját, mely a következő lépésekből áll:

      1. A 195.56.150.103 kezdő oldal elérése (HTTP GET request)
      2. A 195.56.150.103/images/accueil1.gif kép elérésnek ellenőrzése, ha azt a megadott időpont óta módosították.
      3. A 195.56.150.103/images/accueil2.gif kép elérésnek ellenőrzése, ha azt a megadott időpont óta módosították.
      4. A 195.56.150.103/images/accueil3.gif kép elérésnek ellenőrzése, ha azt a megadott időpont óta módosították.
      5. A felhasználó ezután 0-20 másodperc közötti véletlen időtartamig várakozik.
      6. A 195.56.150.103/index.en.html oldal betöltése.

 

Tsung konfigurációs lehetőségek

Az előző példában bemutatott XML mellett további lehetőségeink vannak még, melyekkel még jobban személyre szabhatjuk teljesítmény tesztjeinket. Az alábbi lista a teljesség igénye nélkül készült, részletekért keresd fel a Tsung konfigurációs XML dokumentációját:

      • Felhasználók felső határának korlátja (maxusers)
      • Dinamikusan és statikusan létrehozható felhasználók
      • Szakaszok maximális futási idejének meghatározása
      • Felhasználók „gondolkozási idejének” beállítása, véletlenszerűség, hibernálás
      • Kapcsolat időtúllépésének megadása
      • Ismételt próbálkozások száma, ha a kapcsolat nem épült fel
      • HTTP, LDAP authentikáció lehetősége
      • MySQL lekérdezések futtatása
      • Variálható munkamenet típusok
      • Külső (CSV) állományok betöltése és feldolgozása
      • Dinamikus változók használata (JSONPath, Regexp, XPath, PostgreSQL, dinmaikus változók)
      • Iterációk megvalósítása (for, repeat, if, foreach)

 

A Tsung paraméterezése és futtatása

A Tsung futtatása meglehetősen egyszerű, azonban mindenképpen a Screen alkalmazás használatával javasolt, hogy ha a VPS-sel elveszne a kapcsolat, akkor is tovább fusson a teszt. Nézzük meg a beépített helpert, ami megfelelően dokumentált, így külön magyarázatot nem is fűznék hozzá:


[root@aion-tsung ~]# $ tsung -h
Usage: tsung <options> start|stop|debug|status
 Options:
   -f <file>     set configuration file (default is ~/.tsung/tsung.xml) (use - for standard input)
   -l <logdir>   set log directory (default is ~/.tsung/log/YYYYMMDD-HHMM/)
   -i <id>       set controller id (default is empty)
   -r <command>  set remote connector (default is ssh)
   -s                  enable erlang smp on client nodes
   -p <max>      set maximum erlang processes per vm (default is 250000)
   -m <file>     write monitoring output on this file (default is tsung.log) (use - for standard output)
   -F                  use long names (FQDN) for erlang nodes
   -w                  warmup delay (default is 10 sec)
   -v                  print version information and exit
   -6                  use IPv6 for Tsung internal communications
   -h                  display this help and exit

Tsung futtatása a /root/.tsung/ könyvtárból, ha a konfigurációs állomány a simple_website_check.xml:


[root@aion-tsung ~]# screen tsung -f simple_website_check.xml start

A parancs hatására egy külön taszkban fog futni a tsung, amennyiben az xml konfigurációs állomány megfelelő szintaktikájú. A validálást a futtatás előtt megteszi a Tsung, ha hibát talál akkor hibakeresési információkkal megszakítja a futtatást. A teszt alatt keletkező napló állományok alapértelmezetten a ~/.tsung/log/YYYYMMDD-HHMM könyvtárba kerülnek, de ez könyvtár a -l paraméterrel módosítható.

 

Futtatás közben

Az 1.6-os verziótól kezdve közvetlen webes monitorozási lehetőséget (dashboard) kapunk a teszt futása alatt, ami nagy segítség abban, hogy lássuk hogyan alakul a teszt folyamata. Így lehetőségünk van még időben beavatkozni, hogyha esetleg a teszt szélsőséges eredményeket hozna vagy a rendszer összeomolna.

A dashboard a Tsung szerveren érhető el a 8091-es porton keresztül az alábbi paraméterezéssel:

{tsung szerver domain/ip}:8091/es/ts_web:status

 

Tsung konzol

A konzolban nem sok információt kapunk vissza a tesztről

Tsung dashboard státusz

Tsung Dashboard működés közben: {domain}:8091/es/ts_web:status

 

Futtatás után: jelentés készítése

Ha a tesztünk lefutott, akkor a naplózáshoz megadott vagy az alapértelmezett (vagy a paraméterként megadott) mappában megtaláljuk a napló (.log) állományokat, az XML konfigurációs állományunk és a csatolt csv-k másolatát is. A mappába belépve kiadva a treport parancsot – amit korábban létrehoztunk – és ezzel legeneráljuk a teszt eredményeihez tartozó HTML jelentést, melyet böngészőben a report.html file megnyitásával tekinthetünk meg.   A jelentés állomány készítése előtt csak a napló állományok és a dashboard HTML oldala található a naplózáshoz kijelölt mappában:

Tsung naplózási mappa

A naplózási mappa tartalma a teszt lefutása után

Tsung treport futtatása

A treport futtatása konzolból

Tsung naplózási mappa teljesítmény teszt

A jelentés készítés után a naplózási mappa tartalma

 

Jelentés felépítése

Az elkészített jelentést egyszerűen böngészővel nyissuk meg és megkapjuk számszerűsítve a teljesítmény teszt eredményét. A jelentés megfelelően részletes ahhoz, hogy meghatározzuk a rendszer töréspontjait, az esetlegesen nem megfelelő folyamatokat vagy azok gyenge pontjait és ezek alapján kód refaktorálással, szerver skálázással vagy fejlesztéssel megerősítsük/felgyorsítsuk az alkalmazásunkat.

Tsung jelentés statisztika

A jelentés statisztikája

 

A bal oldali menü két nagyobb szakaszra bomlik a felső részben vannak a számszerűsített statisztikák:

 

Main Statistics

      • Transactions: A tranzakciók összefoglalása
      • Network Throughput: A hálózat áteresztő képessége (sebesség/adatmennyiség)
      • Counters: Ferlhasználók, sikeres csatlakozás, lefutott fázisok, stb.
      • Server monitoring: A monitorozás eredménye
      • HTTP status: A HTTP állapot kódok
      • Errors: A futás folyamán fellépő hibák

A második szakaszban az egyes grafikonokat nézhetjük meg, azonban ezt én még meglehetősen kezdetlegesnek érzem.

Graphs

      • Response times: Válaszidők változása a teszt alatt.
      • Throughput graphs: A hálózat terhelésének változása a teszt alatt.
      • Simultaneous Users: A teszt alatt szimulált felhasználók viselkedése.
      • Server monitoring: Az opercáiós rendszer CPU, memória terhelése
      • HTTP status: A válasz HTTP kódok a tesztelés alatt.
      • Errors: A kapott hibák a tesztelés alatt.

 

Tsung jelentés grafikonok

A Tsung jelentés grafikonjai

 

ÖSSZEFOGLALÁS

A teljesítmény tesztelés kiemelten fontos az e-kereskedelmi megoldásoknál, főleg a Magento webáruházaknál, hiszen a felhasználók gyorsan, akadály- és hibamentesen akarnak eljutni a vásárlás végéig és megkapni termékeiket. Főleg ünnepnapok, akciók és jól célzott marketingaktivitás közben és után számítanunk kell komoly terhelésre, így azoknak, akik szeretnék a vásárlóikat megtartani és nem arról híressé/hírhedtté válni, hogy a webáruházuk elérhetetlen, erősen javasolt a teljesítmény tesztelés.

A Tsung tökéletesen alkalmas eszköz arra, hogy teljesítmény teszteket végezzünk, akár az egész áruházra, akár az egyes folyamatokra nézve (adatbázis terhelés, fizetés, stb.), mindezt a legismertebb és leginkább használt protokollok támogatása mellett. Könnyű konfigurálása miatt ideális, hogy rövid tanulással is komoly, minden tekintetben profi teszteket futtassunk. Az automatikus jelentés készítésével pedig mindezt – grafikonokkal színesítve – „laikus” emberek számára is érthető formába önthetjük.

 

Magento 2 modul fejlesztés lépésről lépésre – 2. rész

Ebben a cikkben a következő témákat öleljük fel:

1) Admin menüpont és táblázat (grid) elkészítése

Első lépésben létrehozunk az adminisztrációs felületen a modulunkhoz tartozó menüpontot. Ezt külön (új) főmenüben is elhelyezhetjük, de célszerű a modul működéséhez igazodva besorolni a már létező főmenüpontok alá. Jelen esetben a Content főmenüpontban helyezzük el a sajátunkat. Ehhez szükségünk lesz egy új file-ra. A menüpontot az app/code/Aion/Test/etc/adminhtml/ könyvtárban lévő menu.xml-ben valósítjuk meg. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Aion_Test::content_elements" title="Test Extension" module="Magento_Backend" sortOrder="10" parent="Magento_Backend::content"
             resource="Magento_Backend::content_elements"/>
        <add id="Aion_Test::aion_test" title="Manage Items" module="Aion_Test" sortOrder="10" parent="Aion_Test::content_elements" action="test/test"
             resource="Aion_Test::test"/>
    </menu>
</config>

A file-ban első lépésben definiálunk egy főelemet, ami nem más, mint az id=Aion_Test::content_elements és a menüpontot ez alá helyezzük el, úgy hogy a menüpont (id=Aion_Test::aion_test) parent-nél a főelemet adjuk meg. Ha mindent jól csináltunk, az adminisztrációs felületen a Content főmenüpontban megjelenik a saját almenüpontunk is. A menüpontnál még fontos megemlíteni az Action=”test/test” paramétert, ami a később kialakítandó adminhtml controller útvonalát hivatott megadni.   A következő lépésben szükséges elkészíteni az adminhtml controllert és layout file-t, ami a grid megjelenítésért felelős. Azonban előtte csinálunk egy abstract controller osztályt, hogy a backend jogosultság kezelést egy helyen valósítsuk meg.   Az abstract controller osztályt az app/code/Aion/Test/Controller/Adminhtml/ könyvtárban lévő Test.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml;

/**
 * Aion manage items controller
 */
abstract class Test extends \Magento\Backend\App\Action
{
    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;

    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Registry $coreRegistry
     */
    public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry)
    {
        $this->_coreRegistry = $coreRegistry;
        parent::__construct($context);
    }

    /**
     * Init page
     *
     * @param \Magento\Backend\Model\View\Result\Page $resultPage
     * @return \Magento\Backend\Model\View\Result\Page
     */
    protected function initPage($resultPage)
    {
        $resultPage->setActiveMenu('Aion_Test::aion_test')
            ->addBreadcrumb(__('Test'), __('Test'))
            ->addBreadcrumb(__('Items'), __(''));
        return $resultPage;
    }

    /**
     * Check the permission to run it
     *
     * @return boolean
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Aion_Test::test_menu');
    }
}

Az abstract controller initPage() függvénye (metódusa) felelős az aktív menüpont beállítása mellett a breadcrumb (morzsa menü) útvonal beállításáért is. A másik fontos függvény az _isAllowed(), ami az adminisztrátor jogosultságot ellenőrzi és kezeli.   Ezután elkészítjük az admin táblázat (grid) kezeléséhez szükséges controllert is, amit az imént említett abstract controller-ből fogunk kiterjeszteni. A controller osztályt az app/code/Aion/Test/Controller/Adminhtml/Test könyvtárban lévő Index.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

class Index extends \Aion\Test\Controller\Adminhtml\Test
{
    /**
     * @var \Magento\Framework\View\Result\PageFactory
     */
    protected $resultPageFactory;

    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Registry $coreRegistry
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context, $coreRegistry);
    }

    /**
     * Index action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $this->initPage($resultPage)->getConfig()->getTitle()->prepend(__('Items'));
        return $resultPage;
    }
}

Az Index controller felelős az admin táblázat (grid) megjelenítésért. A Magento 2.0-ban minden controller osztály (file) valójában egy action-nek felel meg. Vagyis jelen esetben a Magento 1.x-ből megismert IndexAction() függvényt immár az execute() függvény helyettesíti. Tehát minden controller action-höz külön controller file tartozik és egyetlen execute() függvény. Ez első látásra felvetheti a kérdést, hogy miért jó ez? Lényegében így a teljes modul kódja jobban átlátható, mint a korábban Magento 1.x-ben lévő controller-ek esetében, amelyek sok esetben a fejlesztés végére hosszú script-et eredményeztek. A controller-ben lévő $resultPage és $this->resultPageFactory helyettesíti Magento 1.x-ből megismert $this->loadLayout() és $this->renderLayout() hívásokat.   Ahhoz, hogy a Magento 2.0 felismerje a létrehozott adminhtml controller-ek útvonalát, ezt definiálnunk kell egy külön fájl-ban. Az útvonal definiálását az app/code/Aion/Test/etc/adminhtml/ könyvtárban lévő routes.xml-ban valósítjuk meg. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="test" frontName="test">
            <module name="Aion_Test" before="Magento_Backend" />
        </route>
    </router>
</config>

A fájlban két lényeges tag és hozzá tartozó paraméter látható. Az első a <router id=”admin”> ami jelzi, hogy ez egy backend útvonal. A második a <route id=”test” frontName=”test”> ahol a frontName határozza meg az elkészült adminhtml controllerek fő útvonalát (az admin url utáni első paramétert). Következő lépésben létre kell hozni a collection-t, ami a fenti admin táblázatot (grid) fogja kiszolgálni adatokkal. A collection-t az app/code/Aion/Model/ResourceModel/Test/Grid/ könyvtárban lévő Collection.php-ban valósítjuk meg. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Model\ResourceModel\Test\Grid;

use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Search\AggregationInterface;
use Aion\Test\Model\ResourceModel\Test\Collection as TestCollection;

/**
 * Collection for displaying grid of Aion Items
 */
class Collection extends TestCollection implements SearchResultInterface
{
    /**
     * @var AggregationInterface
     */
    protected $aggregations;

    /**
     * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param string $mainTable
     * @param string $eventPrefix
     * @param string $eventObject
     * @param string $resourceModel
     * @param string $model
     * @param string|null $connection
     * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource
     *
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        $mainTable,
        $eventPrefix,
        $eventObject,
        $resourceModel,
        $model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
        $connection = null,
        \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
    ) {
        parent::__construct(
            $entityFactory,
            $logger,
            $fetchStrategy,
            $eventManager,
            $storeManager,
            $connection,
            $resource
        );
        $this->_eventPrefix = $eventPrefix;
        $this->_eventObject = $eventObject;
        $this->_init($model, $resourceModel);
        $this->setMainTable($mainTable);
    }

    /**
     * @return AggregationInterface
     */
    public function getAggregations()
    {
        return $this->aggregations;
    }

    /**
     * @param AggregationInterface $aggregations
     * @return $this
     */
    public function setAggregations($aggregations)
    {
        $this->aggregations = $aggregations;
    }


    /**
     * Retrieve all ids for collection
     * Backward compatibility with EAV collection
     *
     * @param int $limit
     * @param int $offset
     * @return array
     */
    public function getAllIds($limit = null, $offset = null)
    {
        return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
    }

    /**
     * Get search criteria.
     *
     * @return \Magento\Framework\Api\SearchCriteriaInterface|null
     */
    public function getSearchCriteria()
    {
        return null;
    }

    /**
     * Set search criteria.
     *
     * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
     * @return $this
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null)
    {
        return $this;
    }

    /**
     * Get total count.
     *
     * @return int
     */
    public function getTotalCount()
    {
        return $this->getSize();
    }

    /**
     * Set total count.
     *
     * @param int $totalCount
     * @return $this
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function setTotalCount($totalCount)
    {
        return $this;
    }

    /**
     * Set items list.
     *
     * @param \Magento\Framework\Api\ExtensibleDataInterface[] $items
     * @return $this
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function setItems(array $items = null)
    {
        return $this;
    }
}

A fájlban definiált osztály felelős a kialakítandó admin táblázatban(grid) az adatok hozzáadásáért, keresés és lapozás implementálásáért. A következőkben leírt UI Components megfelelő működéséhez szükséges a fenti függvények implementálása. Másik előnye, hogy az itt definiált osztályt könnyen máshol is fel tudjuk használni, amennyiben máshol is megszeretnénk jeleníteni a modul adatait admin táblázatban(grid), például egy product vagy customer ajax tab-on az adminisztrációs felületen.   Már csak egy dolog van hátra, ami nem más, mint az Index controllerhez tartozó layout file elkészítése. A layout file-t az app/code/Aion/Test/view/adminhtml/layout/ könyvtárban lévő test_test_index.xml-ban valósítjuk meg. Jól látható a korábban definiált route a fájl nevében: alap route -> könyvtár -> controller action. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="test_test_listing"/>
        </referenceContainer>
    </body>
</page>

A layout fájlban lévő „content” referencia konténerben van definiálva a korábban említett UI component file neve, amire a következő pontban térünk ki részletesen.

2) UI Components avagy az admin táblázat (grid) új kialakítása

A Magento 2.0-ban bevezetett UI component-ek használatával sokkal könnyebben tudunk admin táblázatot (grid) létrehozni és emellett az adminisztrátor számára sokkal több lehetőséget nyújt a táblázatban történő kereséshez, szűréshez, az oszlopok tetszőleges megjelenítéséhez. Emellett külön megjelenítések menthetők le.   A UI component-ek működéséhez több fájlt is létre kell hoznunk és megfelelően implementálnunk. A legfontosabb fájl, ami az admin táblázat működését és megjelenését meghatározza egy xml fájl. Ez a mi modulunk esetében a test_test_listing.xml nevet kapta (lásd előző pont) és az app/code/Aion/Test/view/adminhtml/ui_component könyvtárban van elhelyezve. A fájl nagyon hosszú, így tartalmát darabolva jelenítjük meg cikkünkben.

<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
            <item name="deps" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">test_test_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Item</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <dataSource name="test_test_listing_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">TestGridDataProvider</argument>
            <argument name="name" xsi:type="string">test_test_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">test_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="update_url" xsi:type="url" path="mui/index/render"/>
                </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
            </item>
        </argument>
    </dataSource>
…

A fájlban lévő első argument tag-ben vannak definiálva az alábbiak:

  • adat forrás név (test_test_listing_data_source) lásd második dataSource tag: <dataSource name=”test_test_listing_data_source”>
  • oszlopok tag neve: test_test_columns, erre továbbiakban lesz szükségünk
  • az új elem hozzáadása és egyéb gombok definiálása, lásd: <item name=”buttons” xsi:type=”array”> tag
…
<container name="listing_top">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="template" xsi:type="string">ui/grid/toolbar</item>
        </item>
    </argument>
    <bookmark name="bookmarks">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="storageConfig" xsi:type="array">
                    <item name="namespace" xsi:type="string">test_test_listing</item>
                </item>
            </item>
        </argument>
    </bookmark>
    <component name="columns_controls">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="columnsData" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns</item>
                </item>
                <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
                <item name="displayArea" xsi:type="string">dataGridActions</item>
            </item>
        </argument>
    </component>
…

Az xml fájlt tovább böngészve definiálásra kerülnek a táblázat felett elhelyezkedő funkciók. Ezek az alábbiak:

  • lementhetjük az aktuális táblázat megjelenést több view-ban, lásd: <bookmark name=”bookmarks”> tag
  • beállíthatjuk túl sok oszlop esetén, hogy melyek jelenjenek meg és az előbb említett bookmarks-nál menthetjük le, lásd: <component name=”columns_controls”> tag
…
<filterSearch name="fulltext">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="provider" xsi:type="string">test_test_listing.test_test_listing_data_source</item>
            <item name="chipsProvider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters_chips</item>
            <item name="storageConfig" xsi:type="array">
                <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">current.search</item>
            </item>
        </item>
    </argument>
</filterSearch>
<filters name="listing_filters">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="columnsProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns</item>
            <item name="storageConfig" xsi:type="array">
                <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">current.filters</item>
            </item>
            <item name="templates" xsi:type="array">
                <item name="filters" xsi:type="array">
                    <item name="select" xsi:type="array">
                        <item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
                        <item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
                    </item>
                </item>
            </item>
            <item name="childDefaults" xsi:type="array">
                <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.listing_filters</item>
                <item name="imports" xsi:type="array">
                    <item name="visible" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.${ $.index }:visible</item>
                </item>
            </item>
        </item>
    </argument>
</filters>
…

Hozzáadásra kerül a text alapú kereső és a táblázat szűrése (filters). Ezek az alábbiak:

  • varchar, text típusú oszlopokban kereshetünk egy input mezőben, lásd: <filterSearch name=”fulltext”> tag
  • minden egyes oszlopot szűrhetünk megfelelő paraméterek szerint view(Aion\Test\Ui\Component\Listing\Column\Test\Options), select, dátum, ID(range), text típusú szűrések, lásd: <filters name=”listing_filters”> tag
…
<massaction name="listing_massaction">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
            <item name="indexField" xsi:type="string">test_id</item>
        </item>
    </argument>
    <action name="delete">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="type" xsi:type="string">delete</item>
                <item name="label" xsi:type="string" translate="true">Delete</item>
                <item name="url" xsi:type="url" path="test/test/massDelete"/>
                <item name="confirm" xsi:type="array">
                    <item name="title" xsi:type="string" translate="true">Delete items</item>
                    <item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
                </item>
            </item>
        </argument>
    </action>
    <action name="disable">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="type" xsi:type="string">disable</item>
                <item name="label" xsi:type="string" translate="true">Disable</item>
                <item name="url" xsi:type="url" path="test/test/massDisable"/>
            </item>
        </argument>
    </action>
    <action name="enable">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="type" xsi:type="string">enable</item>
                <item name="label" xsi:type="string" translate="true">Enable</item>
                <item name="url" xsi:type="url" path="test/test/massEnable"/>
            </item>
        </argument>
    </action>
</massaction>
…

A massaction tag-en belül kerülnek hozzáadásra tetszőleges általunk definiált műveletek, melyek a tömeges adatmódosításra szolgálnak. A modulunkban ezek az alábbiak:

  • tömeges törlés, lásd: <action name=”delete”> tag
  • tömeges engedélyezés és tiltás, lásd: <action name=”disable”> és <action name=”enable”> tag-ek. Ezek a modulunk adatbázis táblájában korában kialakított is_active adatot módosítják.
…    
    <paging name="listing_paging">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current.paging</item>
                </item>
                <item name="selectProvider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns.ids</item>
            </item>
        </argument>
    </paging>
</container>
…

A <paging name=”listing_paging”> tag implementálja lapozást és a listázott elemek számának választhatóságát (select) a táblázatunkban.

…
<columns name="test_test_columns">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="storageConfig" xsi:type="array">
                <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">current</item>
            </item>
        </item>
            <item name="childDefaults" xsi:type="array">
                <item name="fieldAction" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.test_test_columns_editor</item>
                    <item name="target" xsi:type="string">startEdit</item>
                    <item name="params" xsi:type="array">
                        <item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
                        <item name="1" xsi:type="boolean">true</item>
                    </item>
                </item>
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">test_test_listing.test_test_listing.listing_top.bookmarks</item>
                    <item name="root" xsi:type="string">columns.${ $.index }</item>
                    <item name="namespace" xsi:type="string">current.${ $.storageConfig.root }</item>
                </item>
            </item>
        </item>
    </argument>
    <selectionsColumn name="ids">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="indexField" xsi:type="string">test_id</item>
            </item>
        </argument>
    </selectionsColumn>
…

Fentiek után következik a táblázat oszlopainak meghatározása, lásd: <columns name=”test_test_columns”> tag. Ennek nevét már a fájl elején definiáltuk. A korábban említett mass action-ökhez beállított ID mező, lásd: <selectionsColumn name=”ids”> tag.

…   
        <column name="test_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">textRange</item>
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">ID</item>
                </item>
            </argument>
        </column>
        <column name="name">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="editor" xsi:type="array">
                        <item name="editorType" xsi:type="string">text</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Name</item>
                </item>
            </argument>
        </column>
        <column name="email">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="editor" xsi:type="array">
                        <item name="editorType" xsi:type="string">text</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Email</item>
                </item>
            </argument>
        </column>
        <column name="creation_time" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Created</item>
                </item>
            </argument>
        </column>
        <column name="update_time" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Modified</item>
                </item>
            </argument>
        </column>
        <column name="sort_order">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="editor" xsi:type="array">
                        <item name="editorType" xsi:type="string">text</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Sort Order</item>
                </item>
            </argument>
        </column>
        <column name="is_active">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="array">
                    <item name="disable" xsi:type="array">
                        <item name="value" xsi:type="string">0</item>
                        <item name="label" xsi:type="string" translate="true">Disabled</item>
                    </item>
                    <item name="enable" xsi:type="array">
                        <item name="value" xsi:type="string">1</item>
                        <item name="label" xsi:type="string" translate="true">Enabled</item>
                    </item>
                </item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="editor" xsi:type="string">select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Status</item>
                </item>
            </argument>
        </column>
        <actionsColumn name="actions" class="Aion\Test\Ui\Component\Listing\Column\TestActions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">test_id</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>

Ezt követően a táblázatban lévő oszlopokat kell meghatároznunk. Az egyes oszlopoknál beállíthatjuk a típust, pl.: text, select, textRange, dateRange stb. Az utolsó oszlop az alap action-öket tartalmazza, lásd: <actionsColumn name=”actions” class=”Aion\Test\Ui\Component\Listing\Column\TestActions”> tag   Ezzel el is készültünk a grid definíciós xml-lel (test_test_listing.xml). A továbbiakban megnézünk néhány osztályt, ami az utolsó oszlopban lévő action-ökért felel.

3) UI component osztályok

Az előző pontban kialakított grid definíciós xml-ben található action oszlop működéséhez szükségünk van egy osztályra, mely a megjelenítést és a működést segítik.   Az első az előző pontban látható <actionsColumn name=”actions” class=”Aion\Test\Ui\Component\Listing\Column\TestActions”> tag-ben látható a TestActions osztály. A fájl az app/code/Aion/Test/Ui/Component/Listing/Column könyvtárban van elhelyezve TestActions.php néven. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Ui\Component\Listing\Column;

use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;

/**
 * Class TestActions
 */
class TestActions extends Column
{
    /**
     * Url path
     */
    const URL_PATH_EDIT = 'test/test/edit';
    const URL_PATH_DELETE = 'test/test/delete';

    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * Constructor
     *
     * @param ContextInterface $context
     * @param UiComponentFactory $uiComponentFactory
     * @param UrlInterface $urlBuilder
     * @param array $components
     * @param array $data
     */
    public function __construct(
        ContextInterface $context,
        UiComponentFactory $uiComponentFactory,
        UrlInterface $urlBuilder,
        array $components = [],
        array $data = []
    ) {
        $this->urlBuilder = $urlBuilder;
        parent::__construct($context, $uiComponentFactory, $components, $data);
    }

    /**
     * Prepare Data Source
     *
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as & $item) {
                if (isset($item['test_id'])) {
                    $item[$this->getData('name')] = [
                        'edit' => [
                            'href' => $this->urlBuilder->getUrl(
                                static::URL_PATH_EDIT,
                                [
                                    'test_id' => $item['test_id']
                                ]
                            ),
                            'label' => __('Edit')
                        ],
                        'delete' => [
                            'href' => $this->urlBuilder->getUrl(
                                static::URL_PATH_DELETE,
                                [
                                    'test_id' => $item['test_id']
                                ]
                            ),
                            'label' => __('Delete'),
                            'confirm' => [
                                'title' => __('Delete "${ $.$data.name }"'),
                                'message' => __('Are you sure you wan\'t to delete a "${ $.$data.name }" record?')
                            ]
                        ]
                    ];
                }
            }
        }

        return $dataSource;
    }
}

Az osztály előállítja a mass action megjelenítéséhez szükséges tömböt a megfelelő formátumban. A file elején lévő konstantsoknál fontos a pontos útvonal meghatározása, hogy a megfelelő adminhtml controller-re mutassanak.

4) Adminhtml controller-ek

A grid teljes működéséhez néhány controller-t még el kell készíteni. Nézzük sorban őket. A tömeges törléshez a massDelete controller-t használjuk. A fájl az app/code/Aion/Test/Controller/Adminhtml/Test/ könyvtárban van elhelyezve MassDelete.php néven. A fájl tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;

/**
 * Class MassDelete
 */
class MassDelete extends \Magento\Backend\App\Action
{
    /**
     * @var Filter
     */
    protected $filter;

    /**
     * @var CollectionFactory
     */
    protected $collectionFactory;

    /**
     * @param Context $context
     * @param Filter $filter
     * @param CollectionFactory $collectionFactory
     */
    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
    {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }

    /**
     * Execute action
     *
     * @return \Magento\Backend\Model\View\Result\Redirect
     * @throws \Magento\Framework\Exception\LocalizedException|\Exception
     */
    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());
        $collectionSize = $collection->getSize();

        foreach ($collection as $item) {
            $item->delete();
        }

        $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}

A controller osztály execute() függvénye – vagyis az action – egy collection-t kap (\Magento\Ui\Component\MassAction\Filter osztálytól), amin végig iterálva törli az elemeket. A tömeges státusz módosításhoz a massEnable és massDisable controller-eket használjuk. A fájlok az app/code/Aion/Test/Controller/Adminhtml/Test/ könyvtárban vannak elhelyezve MassEnable.php és MassDisable.php néven. A fájlok tartalma:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;

/**
 * Class MassEnable
 */
class MassEnable extends \Magento\Backend\App\Action
{
    /**
     * @var Filter
     */
    protected $filter;

    /**
     * @var CollectionFactory
     */
    protected $collectionFactory;

    /**
     * @param Context $context
     * @param Filter $filter
     * @param CollectionFactory $collectionFactory
     */
    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
    {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }

    /**
     * Execute action
     *
     * @return \Magento\Backend\Model\View\Result\Redirect
     * @throws \Magento\Framework\Exception\LocalizedException|\Exception
     */
    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());

        foreach ($collection as $item) {
            $item->setIsActive(true);
            $item->save();
        }

        $this->messageManager->addSuccess(__('A total of %1 record(s) have been enabled.', $collection->getSize()));

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}
<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Aion\Test\Model\ResourceModel\Test\CollectionFactory;

/**
 * Class MassDisable
 */
class MassDisable extends \Magento\Backend\App\Action
{
    /**
     * @var Filter
     */
    protected $filter;

    /**
     * @var CollectionFactory
     */
    protected $collectionFactory;

    /**
     * @param Context $context
     * @param Filter $filter
     * @param CollectionFactory $collectionFactory
     */
    public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
    {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }

    /**
     * Execute action
     *
     * @return \Magento\Backend\Model\View\Result\Redirect
     * @throws \Magento\Framework\Exception\LocalizedException|\Exception
     */
    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());

        foreach ($collection as $item) {
            $item->setIsActive(false);
            $item->save();
        }

        $this->messageManager->addSuccess(__('A total of %1 record(s) have been disabled.', $collection->getSize()));

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }
}

A két controller működése nagyon hasonló. Mindkettő a Filter osztálytól kapott collection-ön iterál végig, és állítja be az is_active data kulcsot massEnbale esetén true-ra, míg massDisable esetén false-ra, majd menti a collection elemeit.

5) Object manager konfigurációs

Ahhoz, hogy az elkészített admin táblázat(grid) megfelelően működjön, meg kell adnunk a forrás adat objektumokat és filter-eket. Ehhez szükségünk lesz egy definíciós xml-re. A fájl az app/code/Aion/Test/etc/ könyvtárban van elhelyezve di.xml néven. A fájl tartalma:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="test_test_listing_data_source" xsi:type="string">Aion\Test\Model\ResourceModel\Test\Grid\Collection</item>
            </argument>
        </arguments>
    </type>
    <type name="Aion\Test\Model\ResourceModel\Test\Grid\Collection">
        <arguments>
            <argument name="mainTable" xsi:type="string">aion_test</argument>
            <argument name="eventPrefix" xsi:type="string">aion_test_grid_collection</argument>
            <argument name="eventObject" xsi:type="string">test_grid_collection</argument>
            <argument name="resourceModel" xsi:type="string">Aion\Test\Model\ResourceModel\Test</argument>
        </arguments>
    </type>
    <virtualType name="TestGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
        <arguments>
            <argument name="appliers" xsi:type="array">
                <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
                <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
            </argument>
        </arguments>
    </virtualType>
    <virtualType name="TestGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
        <arguments>
            <argument name="collection" xsi:type="object" shared="false">Aion\Test\Model\ResourceModel\Test\Collection</argument>
            <argument name="filterPool" xsi:type="object" shared="false">TestGirdFilterPool</argument>
        </arguments>
    </virtualType>
</config>

Ebben a fájlban definiáljuk a grid-hez szükséges collection-t (lásd: <item name=”test_test_listing_data_source” xsi:type=”string”>Aion\Test\Model\ResourceModel\Test\Grid\Collection</item>), filter-t és data provider-t, mely UI component megfelelő működéséhez szükséges.   Az egyes elemek editálását, mentését és egyenkénti törlését a következőkben írjuk le.

6) Editáláshoz szükséges admin block-ok létrehozása

Ahhoz, hogy a modulhoz tartozó adatokat létre tudjuk hozni az admin felületen és szerkeszteni tudjuk, szükségünk lesz a megfelelő osztályokra. Első lépésben a container osztályt kell létrehozni, mely később a form-ot fogja tartalmazni. Az osztályt az Aion/Test/Block/Adminhtml/ könyvtárban lévő Test.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block\Adminhtml;

/**
 * Adminhtml Aion items content block
 */
class Test extends \Magento\Backend\Block\Widget\Grid\Container
{
    /**
     * @return void
     */
    protected function _construct()
    {
        $this->_blockGroup = 'Aion_Test';
        $this->_controller = 'adminhtml_test';
        $this->_headerText = __('Items');
        $this->_addButtonLabel = __('Add New Item');
        parent::_construct();
    }
}

Az osztályban lényeges a megfelelő blockGroup és controller meghatározása.   A következő lépésben szükségünk lesz a form container osztályra. Itt határozzuk meg szerkesztetés alatt álló objektum admin oldalának title-jét, és adhatunk hozzá tetszőleges button-okat az alap gombokon kívül, vagy távolíthatunk el. Az osztályt az Aion/Test/Block/Adminhtml/Test könyvtárban lévő Edit.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block\Adminhtml\Test;

/**
 * Aion item edit form container
 */
class Edit extends \Magento\Backend\Block\Widget\Form\Container
{
    /**
     * Core registry
     *
     * @var \Magento\Framework\Registry
     */
    protected $_coreRegistry = null;

    /**
     * @param \Magento\Backend\Block\Widget\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Widget\Context $context,
        \Magento\Framework\Registry $registry,
        array $data = []
    ) {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    /**
     * @return void
     */
    protected function _construct()
    {
        $this->_objectId = 'test_id';
        $this->_blockGroup = 'Aion_Test';
        $this->_controller = 'adminhtml_test';

        parent::_construct();

        $this->buttonList->update('save', 'label', __('Save Item'));
        $this->buttonList->update('delete', 'label', __('Delete Item'));

        $this->buttonList->add(
            'saveandcontinue',
            [
                'label' => __('Save and Continue Edit'),
                'class' => 'save',
                'data_attribute' => [
                    'mage-init' => ['button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form']],
                ]
            ],
            -100
        );

    }

    /**
     * Get edit form container header text
     *
     * @return \Magento\Framework\Phrase
     */
    public function getHeaderText()
    {
        if ($this->_coreRegistry->registry('test_item')->getId()) {
            return __("Edit Block '%1'", $this->escapeHtml($this->_coreRegistry->registry('test_item')->getName()));
        } else {
            return __('New Item');
        }
    }
}

Amennyiben WYSWYG editort is szeretnénk használni például textarea típusú mezőhöz, akkor azt a _construct() függvényben kell elhelyezni, vagy a prepareLayout() függvényben. Az osztályban lévő getHeaderText() függvény határozza meg az admin oldal title értékét.   Az utolsó block, amit el kell készítenünk, a form megjelenítését és kezelését végzi. Az osztályt az Aion/Test/Block/Adminhtml/Test/Edit könyvtárban lévő Form.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Block\Adminhtml\Test\Edit;

/**
 * Adminhtml Aion item edit form
 */
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
    /**
     * @var \Magento\Cms\Model\Wysiwyg\Config
     */
    protected $_wysiwygConfig;

    /**
     * @var \Magento\Store\Model\System\Store
     */
    protected $_systemStore;

    /**
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Framework\Data\FormFactory $formFactory
     * @param \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig
     * @param \Magento\Store\Model\System\Store $systemStore
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Data\FormFactory $formFactory,
        \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
        \Magento\Store\Model\System\Store $systemStore,
        array $data = []
    ) {
        $this->_wysiwygConfig = $wysiwygConfig;
        $this->_systemStore = $systemStore;
        parent::__construct($context, $registry, $formFactory, $data);
    }

    /**
     * Init form
     *
     * @return void
     */
    protected function _construct()
    {
        parent::_construct();
        $this->setId('test_form');
        $this->setTitle(__('Item Information'));
    }

    /**
     * Prepare form
     *
     * @return $this
     */
    protected function _prepareForm()
    {
        $model = $this->_coreRegistry->registry('test_item');

        /** @var \Magento\Framework\Data\Form $form */
        $form = $this->_formFactory->create(
            ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
        );

        $form->setHtmlIdPrefix('item_');

        $fieldset = $form->addFieldset(
            'base_fieldset',
            ['legend' => __('General Information'), 'class' => 'fieldset-wide']
        );

        if ($model->getId()) {
            $fieldset->addField('test_id', 'hidden', ['name' => 'test_id']);
        }

        $fieldset->addField(
            'name',
            'text',
            [
                'name' => 'name',
                'label' => __('Name'),
                'title' => __('Name'),
                'required' => true
            ]
        );

        $fieldset->addField(
            'email',
            'text',
            [
                'name' => 'email',
                'label' => __('Email'),
                'title' => __('Email'),
                'required' => true,
                'class' => 'validate-email'
            ]
        );

        $fieldset->addField(
            'is_active',
            'select',
            [
                'label' => __('Status'),
                'title' => __('Status'),
                'name' => 'is_active',
                'required' => true,
                'options' => ['1' => __('Enabled'), '0' => __('Disabled')]
            ]
        );
        if (!$model->getId()) {
            $model->setData('is_active', '1');
        }

        $fieldset->addField(
            'sort_order',
            'text',
            [
                'name' => 'sort_order',
                'label' => __('Sort Order'),
                'title' => __('Sort Order'),
                'required' => false
            ]
        );

        $form->setValues($model->getData());
        $form->setUseContainer(true);
        $this->setForm($form);

        return parent::_prepareForm();
    }
}

Az osztály _prepareForm() függvényében adjuk hozzá a szerkesztésre szánt mezőket, ami a mi esetünkben a name, email és sort_order mezők. Ezek mellett szerepel még a multistore kezelés szempontjából fontos store_id field is, illetve is_active field is, ami jelen esetben select típusú és a szerkesztés alatt álló elem státuszát hivatott beállítani.   A fent említett három osztállyal el is készítettük az adminisztrációs felületen történő szerkesztéshez szükséges fájlokat.

7) Controller-ek és layout létrehozása

Az editálás megvalósításához a fenti osztályokon kívül szükségünk lesz még a megfelelő controller osztályokra és layout fájlokra.   Az első osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő NewAction.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

class NewAction extends \Aion\Test\Controller\Adminhtml\Test
{
    /**
     * @var \Magento\Backend\Model\View\Result\ForwardFactory
     */
    protected $resultForwardFactory;

    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Registry $coreRegistry
     * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
    ) {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context, $coreRegistry);
    }

    /**
     * Create new item
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        /** @var \Magento\Framework\Controller\Result\Forward $resultForward */
        $resultForward = $this->resultForwardFactory->create();
        return $resultForward->forward('edit');
    }
}

Az osztály az új elemek létrehozására szolgál és lényegében az action függvénye (execute()) átirányít az Edit controller osztályra.   A következő lépésben létrehozzuk a szerkesztéshez szükséges controller-t. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Edit.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

class Edit extends \Aion\Test\Controller\Adminhtml\Test
{
    /**
     * @var \Magento\Framework\View\Result\PageFactory
     */
    protected $resultPageFactory;

    /**
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Registry $coreRegistry
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context, $coreRegistry);
    }

    /**
     * Edit item
     *
     * @return \Magento\Framework\Controller\ResultInterface
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    public function execute()
    {
        // 1. Get ID and create model
        $id = $this->getRequest()->getParam('test_id');
        $model = $this->_objectManager->create('Aion\Test\Model\Test');

        // 2. Initial checking
        if ($id) {
            $model->load($id);
            if (!$model->getId()) {
                $this->messageManager->addError(__('This item no longer exists.'));
                /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
                $resultRedirect = $this->resultRedirectFactory->create();
                return $resultRedirect->setPath('*/*/');
            }
        }
        // 3. Set entered data if was error when we do save
        $data = $this->_objectManager->get('Magento\Backend\Model\Session')->getFormData(true);
        if (!empty($data)) {
            $model->setData($data);
        }

        // 4. Register model to use later in blocks
        $this->_coreRegistry->register('test_item', $model);

        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();

        // 5. Build edit form
        $this->initPage($resultPage)->addBreadcrumb(
            $id ? __('Edit Item') : __('New Item'),
            $id ? __('Edit Item') : __('New Item')
        );
        $resultPage->getConfig()->getTitle()->prepend(__('Items'));
        $resultPage->getConfig()->getTitle()->prepend($model->getId() ? $model->getName() : __('New Item'));
        return $resultPage;
    }
}

Az edit action(execute() függvény) első lépésben lekéri a test_id paramétert. Ezt követően inicializálja az Aion/Test/Model/Test modell osztályt. Amennyiben a test_id paraméternek van értéke, a modellt megpróbálja betöltelni az említett id-val. Sikertelen esetben hibaüzenet állít be, majd visszairányít. Ellenkező esetben a betöltött modellt a registry-ben tárolja ($this->_coreRegistry->register(’test_item’, $model)). Ezt olvassa ki a registry-ből a fent már említett form container osztály is, és használja fel. Végezetül létrehozza az oldalt ($resultPage), majd beállítja az oldal title paraméterét és breadcrumb-ot is.   A controller-hez tartozó layout fájlt az Aion/Test/view/adminhtml/layout/ könyvtárban lévő test_test_edit.xml fájlban valósítjuk meg:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="editor"/>
    <body>
        <referenceContainer name="content">
            <block class="Aion\Test\Block\Adminhtml\Test\Edit" name="test_test_edit"/>
        </referenceContainer>
    </body>
</page>

A következő lépés a mentés elkészítése. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Save.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

class Save extends \Aion\Test\Controller\Adminhtml\Test
{
    /**
     * Save action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        // check if data sent
        $data = $this->getRequest()->getPostValue();
        if ($data) {
            $id = $this->getRequest()->getParam('test_id');
            $model = $this->_objectManager->create('Aion\Test\Model\Test')->load($id);
            if (!$model->getId() && $id) {
                $this->messageManager->addError(__('This item no longer exists.'));
                return $resultRedirect->setPath('*/*/');
            }

            // init model and set data

            $model->setData($data);

            // try to save it
            try {
                // save the data
                $model->save();
                // display success message
                $this->messageManager->addSuccess(__('You saved the item.'));
                // clear previously saved data from session
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData(false);

                // check if 'Save and Continue'
                if ($this->getRequest()->getParam('back')) {
                    return $resultRedirect->setPath('*/*/edit', ['test_id' => $model->getId()]);
                }
                // go to grid
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                // display error message
                $this->messageManager->addError($e->getMessage());
                // save data in session
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData($data);
                // redirect to edit form
                return $resultRedirect->setPath('*/*/edit', ['test_id' => $this->getRequest()->getParam('test_id')]);
            }
        }
        return $resultRedirect->setPath('*/*/');
    }
}

A controller osztály első lépésben várja a korábban kialakított form által posztolt adatokat ($data = $this->getRequest()->getPostValue();). Amennyiben ez nem egy üres tömb, inicializálja az Aion/Test/Model/Test modell osztályt és ha létezik a paraméterként kapott test_id is (vagyis nem új objektum mentésére kerül sor), akkor betölti a megfelelő id-val. Ezt követően a post-ban kapott adatokat beállítja majd menti a modellt. Amennyiben mindezzel elkészültünk, akkor a korábban kialakított admin grid-ből (táblázat) új objektumokat tudunk hozzáadni és menteni, majd ezeket szerkeszteni. Még egy fontos controller van hátra, ami a törlést hivatott megvalósítani. Az osztályt az Aion/Test/Controller/Adminhtml/Test/ könyvtárban lévő Delete.php fájlban valósítjuk meg:

<?php
/**
 * Copyright © 2016 AionNext Ltd. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Aion\Test\Controller\Adminhtml\Test;

class Delete extends \Aion\Test\Controller\Adminhtml\Test
{
    /**
     * Delete action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        // check if we know what should be deleted
        $id = $this->getRequest()->getParam('test_id');
        if ($id) {
            try {
                // init model and delete
                $model = $this->_objectManager->create('Aion\Test\Model\Test');
                $model->load($id);
                $model->delete();
                // display success message
                $this->messageManager->addSuccess(__('You deleted the item.'));
                // go to grid
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                // display error message
                $this->messageManager->addError($e->getMessage());
                // go back to edit form
                return $resultRedirect->setPath('*/*/edit', ['test_id' => $id]);
            }
        }
        // display error message
        $this->messageManager->addError(__('We can\'t find the item to delete.'));
        // go to grid
        return $resultRedirect->setPath('*/*/');
    }
}

A delete action (execute() függvény) első lépésben lekéri a test_id paramétert. Ezt követően inicializálja az Aion/Test/Model/Test model osztályt. Amennyiben a test_id paraméternek van értéke, a modellt megpróbálja betölteni az említett id-val, majd elvégzi a törlést.   Bízom benne, hogy ebben a kétrészes átfogó cikkben sikerült átadnom mindazt a tudnivalót, mellyel sikeresen hozhatsz létre te is egy saját modult a Magento 2 rendszerben, és állíthatsz be, illetve szerkeszthetsz különböző elemeket, fájlokat hozzá, mint pl. adatbázis tábla, modell, collection, block, admin táblázat, layout stb.   A cikk első része itt olvasható: Magento 2 modul fejlesztés lépésről lépésre – 1. rész

 

Amit mindenképp tudnod kell az e-kereskedelmi blogolásról

Meggyőzünk a számokkal

Mégis, mennyire éri meg blogolni? Mit érhetünk el azzal, hogy szöveget helyezünk egy olyan oldalra, ahol semmi keresnivalója nem lenne – hiszen a cél az, hogy a beérkező látogatókat a termékhez irányítsuk, konverzióra bírjuk, és nem az, hogy olvasgassanak! Rendben, ha egy kicsit is otthonosan mozgunk az online marketingben, tudhatjuk, hogy az új látogatók vonzásának egyik legjobb eszköze éppen a tartalom. Ahol a direkt reklámok kudarcot mondanak, ott a content képes arra, hogy nagy mennyiségű új emberhez juttasson el minket.

Látogatók

Átlagosan a blogra érkező látogatók 80%-a először jár ott – ami azt jelenti, hogy egy nagyszerű eszközt kapsz arra, hogy megragadd azokat, akik még nem ismerik a webáruházadat. Az új vásárlók megnyerése mindig nehezebb (és költségesebb), mint a meglévők megtartása. Azzal, hogy értékes tartalmat szolgáltatsz nekik, máris megtetted az első lépést. Webáruház blog látogatók Nem elhanyagolandó az a tény sem, hogy egy bloggal rendelkező céges oldal 55%-kal több látogatót vonz. A felhasználók keresik az információt, az érdekes tartalom megragadja őket, a számukra releváns szövegeket szívesen olvassák.

Linkek

Hogyan hat egy blog a linképítésre? Elképesztően hatékonyan. A blogoló cégek átlagosan 97%-kal több bejövő (inbound) hivatkozást képesek generálni, mint nem blogoló versenytársaik. Még jobb, ha videókat is készítesz, és azokat megosztod a céges blogban, a videót tartalmazó posztok ugyanis 3-szor annyi hivatkozást generálnak, mint az anélküliek. Szintén hatékony a SEO-t (keresőoptimalizálást) tekintve, hogy minél több posztot teszünk közzé, annál könnyebben megtalálhatóvá válunk. Ha minőségi tartalmat teszel közzé, amelyet a felhasználók elolvasnak, megosztanak, akkor egyre több oldalad kerül majd előkelő helyezésekbe a keresők találati oldalain.

Leadek

De hogyan lesz mindebből pénz? Nos, a statisztikák szerint egy bogot vezető B2B vállalat 67 százalékkal több leadet generál havonta, mint egy blogot nem használó. Beérkező megkeresések Az online marketingesek több mint kétharmada az öt leghatékonyabb leadgeneráló eszköz közé sorolja a blogokat, 2015-ben pedig 68 százalékuk tervezte, hogy fokozza blogolási tevékenységét. Azt említettük már, hogy azok a marketingesek, akik előtérbe helyezik a blogolást, 13-szor nagyobb eséllyel élvezhetnek pozitív ROI-t?

Mit és hogyan írj?

Egy céges blog haszna az e-kereskedelemben felbecsülhetetlen: egy olyan saját csatornát nyersz vele, ahol a legfontosabb iparági híreket közölheted, brandedet építheted, ahol gyakori kérdéseket válaszolhatsz meg, ahol anélkül írhatsz releváns és hasznos szövegeket termékeidről (amelyek aztán sok vásárlót győzhetnek meg arról, hogy érdemes pénzt kiadniuk), hogy „nyomulva” akarnál eladni bármit is. Használhatod egyfajta ügyfélszolgálati platformként is: közzétehetsz itt pozitív véleményeket, testimonialokat, amelyek segítenek megbízhatóbb színben feltüntetni téged. Kezelheted nyilvánosan a kifogásokat, reklamációkat, a transzparenciával rokonszenvet érve el és egyben eloszlatva az új, potenciális vásárlók bizonyos kétségeit a céggel vagy a termékkel kapcsolatban. A blog segítségével nemcsak egyszerűen kommunikálhatsz a vásárlókkal. Ha megfelelően érdekesen írsz, közönséget építhetsz, amely már csak azért is visszajár majd, hogy írásaidat olvassa – attól függetlenül, hogy éppen akarnak-e tőled vásárolni.

Így találhatsz jó témákat…

Mielőtt elkezdenél blogolni, érdemes felmérned célközönségedet. Mindenekelőtt nézd meg, hogy milyen kérdésekre, kifejezésekre, kulcsszavakra keresnek rá a keresőmotorokat használva – így máris rengeteg témát találsz, a melyeket érdemes lehet feldolgozni. Blogbejegyzés ötletek összeírása Keresd meg a releváns csoportokat a közösségi médiában, és figyeld meg, miről beszélgetnek a felhasználók. Milyen témákban kérnek segítséget a fórumokon? Melyek azok a kérdések, amelyek viszonylag gyakran felmerülnek? Ezek megválaszolására alaposan kidolgozott, részletes bejegyzéseket készíthetsz, amelyek nemcsak SEO-szempontból erősítenek majd téged, hanem azzal, hogy releváns és értékes válaszokat adsz, szakértői státuszodat is megerősítik a célközönséged szemében. Remek témákat találhatsz akkor is, ha az olyan oldalakat tekinted át, amelyek kifejezetten a kérdés-válasz formátumra épülnek. Itthon a legismertebb ilyen talán a hírhedt Gyakorikérdések, külföldön a szakemberek az igen magas színvonalú Quorát célozzák meg legelőször. Optimális esetben soha nem fogysz ki a témából, de a folyamatot egyszerűbbé is teheted. Egyszerre soha ne csak egy témát keress, hanem egy-két tucatnyit, hogy jó előre felkészülhess. Így több időd marad arra is, hogy forrásokat gyűjts, utánaolvass, aminek kell, hogy kiforrottabb bejegyzéseket készíts. Használj publikációs naptárat: határozd meg, legalább két hétre előre, hogy mikor milyen témát dolgozol majd ki, mikor jelennek majd meg a bejegyzéseid a blogon.

Stílus, hangvétel

Hogy milyen stílusban akarsz kommunikálni, rajtad áll – emberközeli, barátságos brandet akarsz építeni, vagy inkább egy távolságtartóbb, keményen szakértői képet mutatnál? Ezt határozd meg és tartsd magad hozzá hosszú távon is. Ne változtass a stílusodon bejegyzésenként: a cél az, hogy egy konzekvens képet alakíts ki magadról, amit megszoknak és felismernek majd azok, akik rendszeresen olvasnak. Fontos az is, hogy a stílus egyezzen a más csatornákon mutatott képeddel. Igyekezz hasonlóan kommunikálni e-mailben, a közösségi médiában és a blogodon is.

Az írásról

Maga a szöveg nem biztos, hogy jó lesz. Sok cég inkább profi szövegírókhoz fordul, hogy céges blogjaikat vezessék, mivel a több ezer órányi tapasztalatot nem mindig pótolja a szenvedély. Miről és hogyan írj a blogodba? Ugyanakkor néhány egyszerű alapelv betartásával eredményes és élvezetes szövegeket készíthetsz te magad is. Némely esetben kifejezetten előny, ha nem szervezed ki az írást – ügyfélkapcsolati jellegű írásoknál, kifogáskezelésnél, a termékek nagyon alapos bemutatásánál nem árt, ha olyan írja a szöveget, akinek a tapasztalatai első kézből származnak. Figyelj arra, hogy olvasmányos szöveget szerkessz: ne használj nagyobb szövegtömböket, néhány soronként kezdj új bekezdést. Használj sok-sok alcímet, amelyek rövidek és felkeltik a figyelmet. Ezeknek a funkciója, hogy a szöveget csak átfutó olvasó könnyen megtalálja azt, ami őt érdekli. Ha például egy adott termékfajtáról írsz, valószínűleg őt egy-két konkrét kérdés érdekli majd, érdemes tehát a szöveget úgy megszerkesztened, hogy ezekre már egy gyors görgetéskor is felfigyeljen. A főbb üzeneteket, mondanivalókat mindig emeld ki, és az sem árt, ha illusztrációkat használsz. Sőt, akár a korábban elkészített videóidat is felhasználhatod: egyes felhasználói csoportok sokkal szívesebben néznek meg egy informatív pár perces videót, minthogy egy hosszabb szakmai jellegű írást elolvassanak (ilyenek például a cégvezetők). Ezért is fontos, hogy…

Mutáld a tartalmakat!

Ha már blogot írsz, akkor használd fel a tartalmaidat más felületeken, más formában is. Egy kiemelkedően jól sikerült szakcikkből infografika készülhet, több hasonló témájú anyagodat összefűzve letölthető e-könyvet szerkeszthetsz. Egy olyan témát, amit a közönség imád, többször, több formában is feldolgozhatsz, sőt, ezt kötelező megtenned. A legjobb blogok is ismétlik önmagukat, és nem véletlenül: felfoghatod ezt egyfajta A/B tesztnek is, ahol a különféle buyer personákat másfajta tartalommal célzod meg, vagy ugyanannál a közönségnél megpróbálhatsz nagyobb sikert elérni.

Terjessz, ahol csak tudsz

Figyelj arra is, hogy minden egyes cikkedet a lehető legtöbb felületen oszd meg. A Facebook oldal például ideális erre: legjobb anyagaidat bizonyos időközönként újra és újra publikálhatod. Kis kísérletezéssel megtudhatod, melyek a számodra legjobb csatornák – az is lehet, hogy neked a legjobb közösségi felületet nem a Facebook jelenti majd, hanem a LinkedIn vagy éppen az Instagram. Ekereskedelem blog tartalom Ne csak a saját oldalaidon publikálj: a különféle csoportokban, fórumokon is oszd meg a bejegyzéseket, ott, ahol a célközönséged organikus módon megtalálja azokat. Érdemes automatizált hírlevelet is küldened. Készíts egy csalit (például egy e-könyvet) amelyért cserébe csak egy e-mail címet kérsz a felhasználótól. Így havonta egy-két alkalommal elküldheted neki a legnépszerűbb vagy éppen számára legrelevánsabb posztokat – és persze termékeidet is ajánlhatod neki.

Nem kell magányosan dolgoznod

A blogod sokkal nagyobb közönséget fog vonzani akkor, ha valamiképpen hatni tudsz az online véleményvezérekre. Ezek lehetnek olyan szerzők, akik fogyasztói blogokat vezetnek, elismert szakértők a szakterületeden vagy éppen netcelebek, akik valamilyen szempontból a webáruházadra nézve releváns tevékenységet végeznek. A legjobb, ha írásaidat ők is megosztják, hivatkozzák. Értelemszerűen ez azt eredményezi, hogy sokkal láthatóbbá válsz, mintha csak saját csatornáidon osztanád a tartalmakat, ezzel úton vagy a viralitás felé. Kapcsolatba léphetsz velük azonban ennél közvetlenebb módon is. Felkérheted őket arra, hogy cikkeket írjanak a blogodra, te magad pedig vendégposztokat küldhetsz más olvasott blogokba. Mindez szintén ingyen forgalmat hoz neked úgy, hogy segít a linkprofilod építésében és ezzel SEO-ban erősít.

A sikerre várnod kell, de megéri

A blogolás olyan stratégia, amely nem egy-két hét alatt hoz neked eredményeket. Hosszú távú befektetés, amely viszont biztosan megtérül, ha odafigyelsz néhány apróságra. Az is lehet, hogy te magad csak egy év után jössz majd rá, hogy valójában miről is szól a blogod. Lehet, hogy úgy vágsz bele, hogy te egy adott témáról szeretnél írni, idővel azonban kiderülhet, hogy a közönséged valami egészen másra vevő. Még akár arra is rájöhetsz, hogy más a közönséged, mint eredetileg gondoltad. A blognak meg kell érnie, elegendő tapasztalatot és főleg adatot kell begyűjtened ahhoz, hogy bármi biztosat állíts róla. Egy év elteltével pontosan tudni fogod, hogy mely témákat, mely tartalomformátumokat kedvelik a célközönségedbe tartozók. Hogy mely posztok generálnak leadeket és melyek csak egyszerűen látogatókat. Hogy milyen stílusban kell szólnod. Ekkorra megerősödsz, hiszen visszatérő látogatóid, olvasóbázisod lesz, új, a webáruházadra mutató linkek erősítik majd a profilodat, híred megy a közösségi médiában is.   Ha néhány hét után már megtérülést számolgatsz, felejtsd is el a blogolást. Olyan stratégia ez, amely a türelmeseket jutalmazza.   Webshop blog ötletek

10 tipp, hogy te is eredményesen blogolhass

  1. Készíts tartalomnaptárat. De légy egyben rugalmas is: ha aktuális eseményekről kell írnod, ha változnak közönséged preferenciái, ne félj belenyúlni a terveidbe. Módosítsd őket bátran az adatok alapján.
  2. Határozd meg a hatékonysági mutatókat. Tudnod kell, mit mérj. Nem mindegy, hogy számodra a látogatók vagy a leadek száma, a sikeres konverziók aránya vagy éppen a rád mutató hivatkozások jelentik a sikert. A KPI-okat világosan le kell fektetned már az elején.
  3. Használj analitikát és elemezz! Rendszeres időközönként nézd meg, hogy melyek azok a posztok, formátumok, időpontok, amelyek jobb eredményeket hoznak, és azokra erősíts!
  4. Jegyezd le az ötleteidet. A legjobb témaötletek véletlenszerűen jutnak eszedbe, gondoskodj róla, hogy ezeket egy helyen összegyűjtsd, és lehetőleg ne egy halom fecnin tedd ezt.
  5. Kifogytál a témákból? Dehogy fogytál. Nézz körül a szakmai oldalakon, a közösségi média releváns csoportjaiban. Használd a BuzzSumót, hogy lásd, mik a legnépszerűbb releváns témák, amelyeket feldolgozhatsz.
  6. Hangold össze blog, social és e-mail stratégiádat. Terjeszd a tartalmaidat minden lehetséges csatornán, hogy minél több embert érj el.
  7. Tedd könnyen elérhetővé. A felhasználó már a webshop nyitóoldaláról vagy akár bármely másik oldaláról könnyedén, egyetlen kattintással érje el a blog főoldalát, az aloldalakról pedig a releváns tartalmakat.
  8. Ne felejtsd a SEO-t. Soha ne a Google-nek írj, de mindig tartsd észben, hogy olyan témákat keress, amikre az emberek ténylegesen rákeresnek. Végezz kulcsszóelemzést, nézd át, milyen long tail kifejezéseket írnak be buyer personáid a keresőbe, és válaszold meg a kérdéseiket.
  9. Ne akarj magányos hős lenni. Ha egyedül akarsz megcsinálni mindet az írástól az analitikán és a dizájnon keresztül a terjesztésig, a tervezést, szervezést, bele fogsz rokkanni a feladatba. Ha kell, bízd meg alkalmas kollégáidat, ha szükséges, fizess meg profikat.

 

Ne add fel. Légy türelemmel és készíts egyre több és több minőségi anyagot. Hidd el: ha megépíted, eljönnek.

 

Magento projektek automata tesztelése Seleniummal

Ebből a cikkből kiderül, hogy: 

  • Miért a Selenium-ot válaszd?
  • Mikor éri meg a legjobban használni?
  • Hogyan és miből épül fel a Selenium?
  • Hogyan töltsd le?
  • Mik a funkciói? Hogyan használd?
  • A cikk legvégén pedig egy egyszerű példával mutatom be a folyamatot.

 

Tarts velem!

magento selenium teszter bea

 

Egy e-kereskedelmi vállalkozónak, napjaink kiélezett versenykörnyezetében, muszáj követnie a piacon történő változásokat, és ő maga is rendszeresen változtatásokat kell, hogy végezzen a honlapon.

Ezek a kisebb változtatások teszik lehetővé, hogy válaszoljon a legújabb trendekre és lépést tartson versenytársaival. Ez a változás lehet egy új termék, ajánlat, kedvezmények, szállítási módok, fizetési módok és még sok más.

A rendszeres módosítások közben, a cégek „zökkenőmentes” felhasználói élményt kell biztosítsanak, különben ez negatív és nem kívánt eredményekhez vezethet. Ennek érdekében a tesztelőknek folyamatosan ellenőrizniük kell a honlapot és kiszűrniük a hibákat. A webfejlesztő cégek agilis módszertan szerint dolgoznak, ahol a „sprint”-ek elég rövidek, és minden változtatás után szükséges átfogó, regressziós teszteteket végezni. Sajnos, amikor a változtatások túl gyakoriak, a manuális tesztelés monotonná válhat, és sok időt vehet igénybe. Előbb-utóbb valamiféle „könnyítést” kell alkalmaznunk.

 

Itt jön képbe a Selenium. Lássuk csak, miért.

 

 

1) Miért a Selenium?

 

SELENIUM:

A Selenium egy Firefox bővítmény, amely Black Box jellegű felületi tesztek készítésére ad lehetőséget. Ez annyit jelent, hogy az alkalmazás egészét teszteljük anélkül, hogy ismernénk annak belső működését. A tesztesetek felvétele a Selenium IDE Firefox plugin segítségével történik, amely automatikusan rögzíti a böngészőben végrehajtott műveleteket.

 

Ez nagyon megkönnyítheti a tesztelők életét, mert a tesztek gyorsan, de mégis  optimális módon fejlődnek és ez nagyon költséghatékony lehet. Továbbá a Selenium IDE egy fantasztikusan „segítőkész” eszköz, mert hatékonyan, gyorsan és robotszerűen hajt végre olyan lépéseket, melyek után az alkalmazás tesztelhetővé válik, és olyan állapotba segíti a szoftvert, amelyet manuálisan unalmas, fáradalmas elérni, tesztelni. Ha automatizálod az utat, mellyel eléred ezt a pontot, akkor nem leszel fáradt és ideges, illetve képes leszel továbbra is a tesztelési tudásod legjavát adni.

 

Az automata tesztek bevezetésének számtalan előnye van. Nézzünk is meg egy párat:

  • Lehetőség van a tesztesetek ismételt visszajátszására
  • A teszteseteket párhuzamosan hajthatjuk végre
  • Felügyelet nélkül is futtathatjuk a teszteket
  • Növeli a pontosságot és nagymértékben csökkenti az emberek által generált hibákat
  • Időt és pénzt takaríthatunk meg

 

Itt felmerül néhány fontos kérdés:

  • Mi a legjobb eszköz arra, hogy automatizáljam a teszteseteimet?
  • Milyen költségekkel jár?
  • Mennyire könnyű alkalmazni?

 

A fenti kérdések tekintetében, a webes alkalmazások esetében, ahogy fent is említettem, a Selenium egy nagyon jó megoldás. Miért?

  • Könnyen használható
  • Ingyenes (Open source)
  • Nagy felhasználói adatbázissal és segítő közösséggel rendelkezik
  • Több programozói nyelvet támogat

 

2) Mikor érdemes használni?

A Selenium IDE egy könnyen használható eszköz egyszerű tesztesetekre, de a komplexebbekre (ahol például több elágazás van) már kevésbé az.

 

A Selenium-os tesztek készítése akkor éri meg a leginkább, amikor a felület már nem nagyon módosul, de a mögöttes logika, szolgáltatások vagy a háttérrendszerek fejlesztése még folyamatban van. Selenium-os felületi tesztek készítésével a tesztek bármikor visszajátszhatók így szükség szerint folyamatosan értesülhetünk a tesztjeink lefutási eredményeiről!

 

3) Hogyan és miből épül fel?

A Selenium nemcsak egy egyedülálló eszköz, melyet csak egymagában lehet használni, sokkal inkább egy különböző teszteszközökből álló csomag.

Ez a csomag a következő komponensekből tevődik össze:

  • Selenium Integrated Development Environment (IDE) 01-selenium-ide-ikon
  • Selenium Remote Control (RC) 02-selenium-rc-ikon
  • Selenium WebDriver
  • Selenium Grid 03-selenium-grid-ikon

 

A felhasználó saját igényei szerint választhat ezek közül az eszközök közül.

04-selenium-csomag

 

  1. Selenium IDE

A Selenium IDE a legegyszerűbb a Selenium Csomag eszközei közül. A Rögzítés és a Visszajátszás funkció teszi kivételesen könnyűvé a megtanulását, hiszen semmilyen vagy csak minimális programozói hozzáértést igényel.

 

  1. Selenium RC (Selenium Remote Control)

A Selenium RC (Selenium 1) egy Java-ban írt eszköz, amely teszt script-ek létrehozását teszi lehetővé webes alkalmazásokhoz, a kívánt programozói nyelven, valamint ezek futtatását is elvégzi.

Ez az eszköz önmagában már nem igazán használatos.

 

  1. Selenium Grid

A Selenium Grid egy új funkciót tesz elérhetővé, mégpedig azt, hogy a Selenium

RC-ben írt teszteket egy időben több böngészőben és platformon futtathatjuk.

 

  1. WebDriver

A WebDriver egy különálló eszköz, de nagyon sok előnye van, főleg a Selenium RC-vel karöltve. A kettő összeolvadását gyakran Selenium 2-nek nevezik. A WebDriver közvetlenül eléri azokat a böngészőket, amelyek támogatják az automatizálást.

 

Támogatott böngészők:

05-selenium-tamogatott-bongeszok

 

Támogatott programozói nyelvek:

06-selenium-tamogatott-programnyelvek

 

Támogatott operációs rendszerek:

07-selenium-tamogatott-operacios-rendszerek

 

4) Hogyan töltsd le?

 

Első lépés:

Nyisd meg a böngészőt (Firefox) és írd be a Seleniumhq URL-t. Ez a hivatalos oldala a Selenium-nak. Nyomj a „Download” fülre, itt megtalálod a Selenium összes eddigi verzióját.

08-selenium-letoltes

 

Második lépés:

A Selenium IDE bejegyzés alatt találsz egy linket, melyre rákattintva a Firefox bővítmény oldalára kerülsz, ahonnan azonnal letölthető és telepíthető.

09-selenium-letoltes-telepites

 

Harmadik lépés:

Klikkelj az „Add to Firefox” gombra.

10-selenium-ide-firefox

 

Negyedik lépés:

Klikkelj a „Telepítés” gombra.

11-selenium-telepitese

 

Ezzel készen is vagyunk, már meg is jelent a böngésződben a jobb felső sarokban a Selenium IDE ikonja. Innen tudod később indítani a programot. Ha ráklikkelsz már meg is jelenik a Selenium ablak.

 

12-selenium-telepites-ide

 

5) A Selenium funkciói és használata

 

Nézzük meg részletesen a menüpontokat és a funkciókat.

Sajnos a Selenium kisebb hibái közé tartozik, hogy egyes elnevezések magyarul, mások pedig angolul jelennek meg, de a csatolt képeken jól látszik minden, remélem így is érthető lesz mindenki számára.

 

1) Menüsor

A menüsor az Selenium ablak felső részén van elhelyezve. Öt részből áll:

  • Fájl
  • Szerkesztés
  • Actions (Műveletek)
  • Options (Opciók)
  • Súgó

 

1.1 Fájl menü

A Fájl menü nagyon hasonló bármely más alkalmazásban használt menühöz. Lehetőséget nyújt a felhasználó számára, hogy:

  • Új tesztesetet hozzon létre, meglévőt kinyisson, vagy elmentse a jelenlegit.
  • Kiexportálhasson egy adott tesztesetet vagy egy teszteset csoportot, a kívánt programozási nyelven, az „Export Test Case As” és „Export Test Suite As” opciókkal. Az első a jelenleg aktív tesztesetet exportálja, míg a második az összes nyitva lévőt.
  • Bezárhassa a tesztesetet

 

13-selenium-menu-fajl

 

A Selenium IDE tesztesetek a következő formátumban menthetőek el:

  • HTML formátum

 

A Selenium IDE tesztesetek a következő formátumban/programozási nyelven exportálhatók:

  • java (Java-ban exportálva)
  • rb (Ruby-ban exportálva)
  • py (Python-ban exportálva)
  • cs (C#-ben exportálva)

 

1.2.  Szerkesztés menü

14-selenium-menu-szerkesztes

 

A Szerkesztés menü olyan opciókat biztosít, mint a Visszavonás, Újra, Kivágás, Másolás, Beillesztés, Törlés és a Minden kijelölése, amelyek általában minden Szerkesztés menüben jelen vannak, valamint ezek mellet még a következők állnak rendelkezésünkre:

  • Insert New Command (Új parancs beszúrása) ‒ ez lehetővé teszi a felhasználó számára, hogy a teszteseten belül felvegyen egy új parancsot
  • Insert New Comment (Új hozzászólás beszúrása) ‒ ez lehetővé teszi a felhasználó számára, hogy a teszteseten belül beszúrjon egy hozzászólást, amivel a későbbi parancsokat magyarázza.

 

Insert New Command

Az új parancs a kijelölt parancs/lépés fölé kerül.

14-selenium-uj-parancs

 

A felhasználó most beszúrhatja az új parancsot, kitöltve a Target (Cél) és Value (Érték) mezőket.

15-selenium-uj-parancs-target-value

 

Insert New Comment

Az új hozzászólás ugyanúgy kerül be, mint a parancs.

16-selenium-uj-hozzaszolas

 

A lila szín jelöli, hogy ez egy hozzászólás/magyarázat.

 

1.3. Actions (Műveletek) menü

17-selenium-muveletek-actions-menu

 

 

 

Az Actions menüben a következő lehetőségek közül választhatunk:

 

  • Record (Rögzítés) – Erre a gombra kattintva a Selenium IDE el kezdi rögzíteni a Firefox böngészőben végrehajtott lépéseket.
  • Play entire test suite – Ez az opció lefuttatja az ehhez a teszt csoporthoz tartozó összes tesztesetet.
  • Play current test case – Ez az opció a jelenleg aktív, kijelölt tesztesetet futtatja le.
  • Pause/Resume – A teszteset futását szüneteltetni és folytatni is lehet.
  • Toggle Breakpoint – A felhasználó beszúrhat egy vagy akár több töréspontot a tesztbe, így bármely lépésnél leállásra kényszerítheti a tesztet.
  • Set/Clear Start Point – Itt egy kezdő pontot adhatunk meg, bármelyik lépéshez, ezután innen fog indulni a teszt.
  • Ugyanitt tudjuk állítani a teszt futásának sebességét is a „Faster, „Fastest”, „Slower” és a „Slowest opciókkal.

 

1.4 Options (Opciók) menü

18-selenium-opciok-options-menu

 

Az Options menü lehetőséget ad a felhasználónak, hogy gyakorolhassa a Selenium IDE által támogatott beállítások kezelését, „játszadozhat” az általános beállításokkal (General), az elérhető formátumokkal (Formats), az elérhető bővítményekkel (Plugins) és az elérhető helymeghatározókkal és azok sorrendjével (Locator Builders).

 

19-selenium-opciok-options-menu-general-altalanos

 

1.5. Súgó menü

 

Ha elakadtunk, itt nézhetjük meg a dokumentációkat, itt jelenthetjük be, ha hibát észleltünk és innen tudunk eljutni a Selenium hivatalos weboldalára vagy blogjára.

 

 

2) Az URL sáv

 

Az URL sáv ugyanolyan és nagyjából úgy működik, mint egy böngészőben. Megjegyzi a korábban látogatott oldalak címét, így később csak ki kell választanunk, ami nagyban megkönnyíti a dolgunkat, főleg, ha már nem tudjuk megjegyezni és észben tartani mindegyik weboldal címét, amin dolgozunk.

20-selenium-url-sav

 

3) Eszköztár

 

21-selenium-eszkoztar

 

Az eszköztár közvetlenül az URL sáv alatt található és a következő opciókat tartalmazza:

 

  • Playback Speed – Itt lehet állítani a teszt futtatásának sebességét. 22-selenium-eszkoztar-playback-speed
  • Play test suite – Ezzel a gombbal az összes tesztesetet futtathatjuk, ami jelen éppen meg van nyitva. 23-selenium-eszkoztar-play-test-suite
  • Play test case – Ezzel a gombbal az éppen aktív tesztesetet futtathatjuk. 24-selenium-eszkoztar-play-test-case
  • Pause – Szüneteltethetjük a teszt futását. 25-selenium-eszkoztar-pause
  • Step – A felhasználó átléphet egy adott parancsot, egyszerre csak egy parancsot végrehajtva. Általában hibajavításnál használatos. 26-selenium-eszkoztar-step
  • Rollup – Összevonhatunk két vagy több lépést. 27-selenium-eszkoztar-rollup
  • Record – Ezzel a gombbal indíthatjuk és le is állíthatjuk a rögzítést. 28-selenium-eszkoztar-record

 

4) Szerkesztő

 

A szerkesztő az az ablak, ahol a teszteset lépéseit láthatjuk, ide kerülnek be az újonnan rögzített parancsok is. A szerkesztőnek két nézete van:

 

1) Táblázat (Table) nézet

29-selenium-tablazat

 

2) Forrás (Source) nézet

Itt a teszteset HTML formátumban jelenik meg.

30-selenium-forras-nezet

 

A Szerkesztőben a felhasználónak lehetősége nyílik begépelni a parancsokat, a Selenium már az első karakter begépelése után felajánlja a lehetséges opciókat.

A Select gombra kattintva kiválaszthatjuk az adott elemet közvetlenül a böngészőből, úgy, hogy ráklikkelünk.

A Find gomb az adott érték alapján rámutat a hozzá tartozó elemre a böngészőben.

31-selenium-find

 

5) Test Case ablak

32-selenium-test-case-ablak

 

Itt fog megjelenni a legújabb teszteset, de mivel a Selenium-ban lehetőség van arra, hogy egyszerre több tesztesetet is megnyissunk, az összes itt lesz felsorolva. Így könnyen kiválaszthatjuk, hogy éppen melyiken szeretnénk dolgozni. Egy teszt-re kattintva a Szerkesztő ablakban megjelennek annak lépései.

Itt jelennek meg a lefuttatott teszt eredményei is, amit a Selenium IDE két színnel jelöl: a pirossal és a zölddel.

  • A Piros szín a hibákat illetve a teszt elbukását jelöli
  • A Zöld szín a sikeres tesztesettel társítható

 

6) Log (Napló) ablak

33-selenium-naplo-log-ablak

 

A Log fülön információkat láthatunk, üzenetek formájában, mindegyik lépésről külön-külön, annak lefutásának pillanatában. Itt jelenik meg az is, ha hiba van a tesztben vagy elbukott, piros színnel jelölve, így a későbbiekben könnyen javíthatjuk a tesztünket.

Itt találhatjuk, többek között a Reference fület is, ahol részletes leírást kapunk egy adott parancsról, és az Expert fület is, amelyet egy kiegészítő ad hozzá a Seleniumhoz. Itt tippek és javítási ötletek jelennek meg, amelyeket azonnal elvégezhetünk az Inspect, majd Fix gombra való kattintással.

Ezzel nagyjából elérkeztünk a bemutatás és az elméleti rész végéhez. Egy rövidebb példával zárnám a cikkemet, hogy gyakorlatban is lássuk a fent leírtakat. Ez tényleg egy egyszerű teszteset, ennél jóval összetettebbekkel is össze lehet állítani, de nem szerettem volna túlbonyolítani, hisz nem ez a cikkem lényege.

 

Példa

 

Egy teszt „script” összeállítása 3 nagy lépésből áll:

  1. Rögzítés – A Selenium IDE rögzít minden kattintást és műveletet, amit a böngészőben hajtottunk végre.
  2. Visszajátszás – A már összerakott tesztesetet többször vissza kell játszanunk, hogy meggyőződjünk arról, hogy jól működik, esetleg javítsuk, csiszoljunk rajta.
  3. Mentés – Ha a teszt megfelelően stabil, ajánlott elmenteni, hogy a jövőben bármikor újra használhassuk.

 

Rögzítés

 

Első lépés

Indítsuk el a Firefox böngészőt és ezzel együtt a Selenium-ot is, az eszköztárban található ikonra kattintva.

 

Második lépés

Írjuk be a tesztelni kívánt weboldal címét (https://accounts.google.com) az URL sávba.

34-selenium-url-tesztelesre

 

Harmadik lépés

A Record gomb alapértelmezetten be van kapcsolva, ha mégsem, ne felejtsük el bekapcsolni, különben nem rögzíti a lépéseket.

 

Negyedik lépés

Nyissuk meg az adott weboldalt (accounts.google.com) Firefox-ban.

 

35-selenium-google-bejelentkezes

 

 

Ötödik lépés

Írjunk be egy létező és valós e-mail címet.

 

Hatodik lépés

Írjuk be az e-mail címhez tartozó jelszót.

 

Hetedik lépés

Kattintsunk a „Sign In” vagy „Bejelentkezés” gombra, a belépés befejezéséhez.

 

Nyolcadik lépés

Végül leállíthatjuk a rögzítést a piros gombbal, és visszajátszhatjuk a tesztesetünket.

 

Figyelem! Ha a tesztesetbe nem rögzítettük bele a Kijelentkezés lépést, a visszajátszás előtt midnenképp szükséges ezt a lépést megtenni, különben a Selenium nem fogja megtalálni a szükséges mezőket vagy gombokat.

 

Visszajátszás

Most, hogy elkészült a tesztünk, szükséges visszajátszani, hogy megnézzük, elég stabil-e. Kattintsunk a „Play current test case” gombra.

 

36-selenium-replay

 

Mentés

Ha minden rendben van, elmenthetjük a script-et, a Fájl -> Mentés másként opcióra kattintva.

 

ÖSSZEGZÉS

Egyértelmű cél, hogy weboldalunk minőségét fokozzuk és pozitív felhasználói élményt nyújtsunk. Ám ezentúl fontos szempont az is, hogy az ezen dolgozó emberek szeressék és élvezzék a munkájukat, hogy a legjobbat nyújthassák.

Összesítésben a Selenium egy nagyon hasznos eszköz, amivel megkönnyíthetjük a tesztelőink munkáját, mellyel jobb minőséget érhetnek el. Ahogy a fentiekből is kiderül, nagyon egyszerű a használata, de sok gyakorlást igényel, ha komplexebb feladatokat kívánunk megoldani.