10 hasznos módszer a Magento-ban rejlő lehetőségek kiaknázására

Ezért fontos, hogy a már meglévő lehetőségeket mind magunk, mind pedig más fejlesztők és a későbbi fejlesztések/bővítések érdekében, a lehető legjobban kihasználjuk. A következő néhány bekezdésben a Block-ok, Layout XML-ek, Installer Script-ek és tempate fájlok (.phtml) esetében előforduló hibákkal, és/vagy olyan kódrészletekkel fog a cikk foglalkozni, amelyeknél formai vagy egyéb kívánnivalók maradtak a kód átadása után. A kifejtett példákat természetesen egyéb módon is meg lehet oldani, ezek csupán felvázolt lehetőségek. A következőkre térünk ki a cikkben részletesebben:

  • Tömb kulcs-érték párok Magento alatt
  • Objektum értékeinek vizsgálata feltételben (beszédes értékek)
  • Magentoban lévő Block típusok kihasználása
  • Kódismétlés a phtml-ben
  • Elkerülhetetlen lekérdezések (hogyan kezeljük ezeket)
  • Collection megjelenítéskori szétválasztás
  • Installer Script-ek hibára futása, és kezelése
  • Collection kapcsolatok, egymásba-ágyazott lekérdezések
  • Module többnyelvűsítésre felkészítése
  • Helperek használata

 

Tömb kulcs-érték párok Magento alatt

Még ha biztosak is vagyunk benne, hogy egy tömbnek adott kulccsal rendelkeznie kell, ez idővel változhat, ezért (is) ilyen egyértelmű hivatkozást ne helyezzünk el a kódban:

$value = $data['key'];

A Magento szinte összes objektumának egyik legmélyebben elhelyezett őse, a Varien_Object, amely osztály magic-method-ain keresztül könnyedén ellenőrizhetőek ezek az értékek. Az említett osztály konstruktorában átadott tömb értékek elérhetőek egy az objektumon hívott $obj->getData(‘key’), vagy a példánál maradva $obj->getKey() metódus hívásával, amely a kulcshoz tartozó érték hiányának esetén NULL-al tér vissza.

$objData = new Varien_Object($data);
$value = $objData->getKey(); //same as $objData->getData('key');

Ezek után pedig egy if(!is_null($value)){…} vagy if(!empty($value)){…} vizsgálattal léphetünk be a szükséges elágazásba, amennyiben az érték nem NULL.

Objektum értékeinek vizsgálata feltételben (beszédes értékek)

If ($objData->getFlag() == 1) {/*...*/}

Az értéket természetesen adott pillanatban a fejlesztő gond nélkül azonosítja, pár hónap elteltével, vagy egy új/más fejlesztő bevonásával azonban már is csak egy szám, jelentés nélkül. Ezzel szemben a beszédes változó és konstans értékekkel nem csak, hogy tudjuk hova kapcsolni, de a forrás module-t is tudjuk azonosítani.

If ($objData->getFlag() == Module_Helper_Data::FLAG_STATUS_ENABLED) {/*...*/}

Még jobb, ha ilyen esetben ezeket a lehetőségeket, a példában status-okat Model-ben, és a hozzá tartozó táblában tároljuk, és a vizsgálatkor már esetleg csak a helperen történő Module_Helper_Data::getEnabledStatusId() hívással ellenőrizzük. Mindkét megoldás helyes, a fő, hogy a vizsgált érték megadja annak forrását és jelentését.

Magentoban lévő Block típusok kihasználása

A Magento-ban nem olyan sok html elem szerepel, mint Block típus, de amelyek igen, azokat használjuk ki. Gyakran fordul elő, hogy olyan kódrészek, amelyek eredetileg „csak egy helyen fognak szerepelni” címkével voltak ellátva, végül még is minimum 3-4 helyen megjelenítésre kerülnek, ami később ha a kód be volt égetve egy phtml-ben például

<select name="my_select" id="mySelect">
    <?php foreach ($items as $item): ?>
        <option value="<?php echo $item->getId() ?>"><?php echo $item->getName() ?></option>
    <?php endforeach; ?>
</select>

Ilyen Block például a Select amely az app\code\core\Mage\Core\Block\Html\Select.php helyen található. Ennek metódusaival szépen paraméterezhető egy lista, amely aztán több helyen is megjeleníthető, és a forrásban is sokkal jobban mutat.

<module_controller_action>
    <reference name="content">
        <block type="aion_module/block_type" name="aion_module_block_type" as="blockalias">
            <block type="core/html_select" name="selectchild" />
        </block>
    </reference>
</module_controller_action>

Ha így döntünk, lehetőség van a paraméterek megadására layout-ban és a reference Block-on történő metódus-híváson keresztül is, magában a metódusban. Mi most ez utóbbi lehetőséget tárgyaljuk. A Block-ban amelyre reference-ként hivatkoztunk létrehozunk egy getSomeSelectHtml metódust, ahol felkészítjük a Select-et a kiíratásra.

public function getSomeSelectHtml()
{
    $select = $this->getChild('selectchild');
    $select->setData(
        array(
            'id' => 'frontend-id',
            'class' => 'frontend-class-one frontend-class-two',
        )
    );
    //select upload with options $select->setOptions() || $select->addOption()
    return $select->toHtml();
}

ha ezzel végeztünk, már csak a phtml fájlban kell megjelenítenünk a listánkat, ahol is egy echo $this->getSomeSelectHtml(); híváson keresztül ezt megtehetjük.

Kódismétlés a phtml-ben

Előfordul, hogy egy már meglévő működést kell kiegészíteni, aminek már van egy template fájlja, de ilyenkor se legyünk restek egy if-esle vagy egy switch-case helyett, a meglévő template mellé létrehozni egy új template fájlt.

Switch ($mode):
    Case 'list':
        Foreach ($entities as $entity): ?>
            <div class="dsadsa"><a href="..."></a><!--...--></div>
        <?php Endforeach;
    Break;
    Case 'grid':
        Foreach ($entities as $entity): ?>
            <div class="dsadsa"><a href="..."></a><!--...--></div>
        <?php Endforeach;
    Break;
Endswitch;

Ezeket a kódokat duplikálás helyett felsőbb szintű block-ba érdemes helyezni, ahol egy az if vagy switch alapján a megfelelőt hívjuk be. Azért is egyszerűbb, illetve tanácsosabb ez a módszer, mert az ilyen elágazásokban gyakran ugyanazok a html részek szerepelnek, egy-egy plusz elágazással, amelyeket szintén alsóbb szintű block-okba rendezve egy olyan szépen strukturált szerkezetet kaphatunk, amiben ha módosítani kell egy megjelenítést, azt egy helyen megtéve, az oldal minden részén módosul. Ezen felül indokolja még ezt a frontend fejlesztők támogatása és maga a kód átláthatósága is.

<block type="something/list" name="something.list">
    <block type="list/grid" name="gridList">
        <block type="list/item" name="gridListItem"/>
    </block>
    <block type="list/row" name="rowList">
        <block type="list/item" name="rowListItem"/>
    </block>
</block>
If ($mode == Module_Helper_Data::MODE_LIST) {
    $block->getChildHtml('rowList'); //amin belül még az item-et is getChildHtml()-el kérjük le
} else if ($mode == Module_Helper_Data::MODE_GRID) {
    $block->getChildHtml('gridList');
}

Elkerülhetetlen lekérdezések (hogyan)

A törekvések ellenére előfordul, hogy a teljesítmény vagy egyéb indok miatt (csakis kényszerítés hatására!), kénytelenek vagyunk direkt, nem pedig collection-ön keresztüli adatbázis lekérdezéseket futtatni. Ez egy szükséges rossz, de ennek is megvan a formai követelménye, a Magento ad lehetőséget mind egy meglévő Collection Select-jének módosítására, mind pedig új, custom lekérdezés létrehozására. Mindkettő esetén a Varien_Db_Select lesz alapesetben az osztálya az objektumnak amit visszakapunk, amin további metódushívásokkal tudunk dolgozni.

[code]$results = $conn->fetchAll("SELECT *, DATE_ADD( tn.`created_at` , INTERVAL tn.`duration_days` * 0.9 * 24 * 60 MINUTE ) as end_date FROM `tablename` as tn INNER JOIN `anothertablename` as atn ON (tn.atn_id = atn.id) WHERE tn.`somecolumn` = 1 GROUP BY atn.`groupcolumn`");[/code]

A fenti példa egy nagyon rövid, meglehetősen egyszerű lekérdezés, de már ez sem átlátható, már ennek is akár többszöri átolvasása szükséges egy olyan fejlesztőtől, aki elsőre látja, ezért az alábbi formára hozzuk:

[code]//in the installer you can get the connection object like this $this>getConnection();
$conn = $collection->getConnection();
$select = $conn->select()
    ->from(
        array('tn' => 'tablename'),
        array(
            '*',
            'end_date' => new Zend_Db_Expr('DATE_ADD( tn.`created_at` , INTERVAL tn.`duration_days` * 0.9 * 24 * 60 MINUTE )')
        )
    )->joinInner(
        array('atn' => 'anothertablename'),'tn.atn_id = atn.id','*'
    )->where(
        'tn.`somecolumn` = ?',1
    )->group('atn.`groupcolumn`');[/code]

Máris sokkal szebb és átláthatóbb, a Select megvan, ideje lekérni a megfelelő rekordokat, melyre a legegyszerűbb módszer a $results = $conn→fetchAll($select); amin már csak végig kell iterálnunk.

Collection megjelenítéskori szétválasztás

Egy táblában több állapotú elem is lehet, ez nem újdonság, és az sem hogy van ahol csak az A máshol pedig csak a B állapotú elemeket kell megjeleníteni egy oldalon. Azonban ezt mindig a collection szinten kell szűrni, nem pedig ciklusban a block-ban, vagy esetleg a template fájlban.

$entities = Mage::getResourceModel('module/some_collection');
$typesA = array();
$typesB = array();
foreach ($entities as $entity):
    if ($entity->getStatus() == 1 || $entity->getStatus() == 2):
        $typesA[] = $entity;
    else:
        $typesB[] = $entity;
    endif;
endforeach;

Ha egy collection-t ciklusban bontunk szét több részre, amit megjelenítünk az oldalon, annak kimenete lehet (oldalakra bontva) egy olyan első oldal, ahol közöljük a felhasználóval, hogy a B állapotú elemből nincs, majd egy olyan 2. vagy 3. oldal, ahol mégis megjelenik B állapotú elemből N darab. Ezenfelül a limit is az egész collection-re számítódik, nem pedig állapotonként, így az csoportokban megjelenő elemek elosztása nem lesz egyenletes.

//filtered for status, both collections limited to 10
$A = $this->getItemsToStatusA(10); 
$B = $this->getItemsToStatusB(10); 

foreach ($A as $item) {/*...*/}
foreach ($B as $item) {/*...*/}

Installer Script-ek hibára futása, és kezelése

Az installerek esetében minden esetben fel kell készíteni az esetleges hibákra. A verzió ne lépjen feljebb, ha nem sikerült mindent megoldani, amit abban meghatároztak. Ez azért probléma, mert nincs minden installer-ben lehetőség az előző installer által hozzáadott összes jellemzőt vagy egyéb elemet leellenőrizni, és nem is a mi feladatunk mások munkájának a leellenőrzése. A mi feladatunk behatárolt.

$installer->startSetup();
try{
    //do what you have to do
} catch(Exception $e){
    Mage::logException($e);
}
$installer->endSetup(); //this method will upgrade the version number

Véleményem szerint az egyik legegyszerűbb módja az előző installer-script sikeres lefutásának vizsgálatára, a module verziószámának ellenőrzése. Ez természetesen feltételezi, hogy a script szintén csak teljes sikeresség esetén lépteti a modul verzióját.

if(Mage::getConfig()->getModuleConfig("Package_MyModule")->version == '1.5.3'){
    $installer->startSetup();
    try{
        //do what you have to do
        $installer->endSetup(); //this method will upgrade the version number
    } catch(Exception $e){
        Mage::logException($e);
    }
}

A másik dolog, amit ki tudunk védeni azzal, hogy csak teljes siker esetén engedjük a verziót növekedni, hogy más installer-ek a sajátunk sikertelensége miatt történő verzió-visszaállítás után ne fussanak le többször, pl. data-installer-ek, amelyek nem vizsgálják, hogy adott cms/page vagy cms/block esetleg létezik-e már.

Collection kapcsolatok, egymásba-ágyazott lekérdezések

Az egymásba ágyazott collection lekérések esetén a folyamat sokkal lassabb. Ha lehet, inkább kevesebb lekérdezéssel, célzottabban próbáljunk meg az erőforrásainkat használni.

$mainCollection = Mage::getModel('module/main')->getCollection()
    ->addFieldToFilter('field', array('in', array('value1', 'value2', 'valueN')));

foreach ($mainCollection as $mainCollectionItem) {
    $subCollection = Mage::getModel('module/sub')->getCollection()
        ->addFieldToFilter('parent', $mainCollectionItem->getSomeField());
    foreach ($subCollection as $subItem) {
        /*...*/
    }
    $subCollection->save();
}

A fenti példában látható módszer időben és teljesítményben is költségesebb, célszerűbb a collection-t már előtte lekérni, és az item-eken végigiterálni. A collection-ok lehetőséget adnak a meglévő elemeik közül visszaadni egy megadott mező értékéhez kapcsolódóan, illetve egy collection megadott mezőinek összes értékét is, amivel ciklus nélkül is lekérhetjük a második collection-t, és így tovább.

$mainCollection = Mage::getResourceModel('module/main_collection')
    ->addFieldToFilter('field', array('in', array('value1', 'value2', 'valueN')));

$subCollection = Mage::getResourceModel('module/sub_collection')
    ->addFieldToFilter('parent', array('in'=>$mainCollection->getColumnValues('some_field')));

foreach ($mainCollection as $mainCollectionItem) {
    $mainSubCollection = $subCollection->getItemsByColumnValue(
        'parent', $mainCollectionItem->getColumnValues('some_field')
    );
    foreach ($mainSubCollection as $subItem) {
        /*...*/
        $subItem->save();
    }
}

Ebben az esetben, ahogy a példában is láthatjuk, arra kell még figyelni, hogy az első esetben míg a végén egy $subCollection->save(); hívással el tudjuk menteni az összes elem változását, addig a második esetben, mivel a $subCollection->getItemsByColumnValue(‘parent’, …) egy tömböt ad vissza, azon nem tudunk metódust hívni, így azt még a ciklusban kell az elemeken egyesével. $subItem->save();

Modul többnyelvűsítésre felkészítése

Nem szükséges a modulnak alapból tudnia minden nyelvet, de még csak az alap nyelven felül sem feltétlen többet, (kivéve ha az anyanyelved nem angol, akkor illik legalább angolra megcsinálni).

<span>
    <label class="main label" for="next_input">This is the label of the input</label>
        <input type="text" name="next_input" id="next_input" placeholder="Next input" />
</span>

Nem kerül erőfeszítésbe, ha modulunkat attól függetlenül, hogy esetleg nincs lefordítva semmilyen nyelvre, erre felkészítve jelenítsük meg a szövegeket, és még csak nem is sokkal hosszabb az ennek megfelelő megjelenítése.

<span>
    <label class="main label" for="next_input"><?php echo $this->__('This is the label of the input'); ?></label>
    <input type="text" name="next_input" id="next_input" placeholder="<?php echo $this->__('Next input'); ?>" />
</span>

Azon felül, hogy a nyelvesítés mindenképp egy plusz pont, ha a module-hoz kapcsolódó szövegeket erre felkészítve adjuk meg, már csak pár mozdulat, hogy a felhasználó a saját nyelvére írhassa át a szövegeket, és nem kell neki szöszölnie, keresgélni a még idegen kifejezéseket a frissen letöltött module-ban.

Helperek használata

Helper hívások szerepelhetnek Block-ban, phtml-ben, de akár Controllerben vagy Model-ben is, a lényeg, hogy a Helperbe olyan adatokat, Konstansokat, metódusokat helyezünk el, amelyeket gyakran használunk, de leginkább egyik részbe sem tartozik. Ilyen például egy config érték Mage::getStoreConfig(‘module_config_path’); vagy config flag Mage::getStoreConfigFlag(‘module_config_path’); lekérése, de egy-egy érték deklarálása CONSTANT MODULE_ENABLED_STATUS = 2; (bár ezek hozzájuk tartozó model-ben is elhelyezhetőek, ha van ilyen), és a kisebb, de több helyen használatos metódusok is kerülhetnek ide.

Összegzés

Kijelenthető, hogy ha előre gondolkodva fejlesztünk, akkor egy gyors, stabil, és átlátható kódot fogunk kapni, amiben mindenki meg fogja találni amit keres, és ha néha ez a bonyolultabb megoldás is, ez többnyire megtérül mikor egy kis fejlesztést 3-4 alkalommal bővítenek ki új működésekkel. Amikor a kód minőségéről van szó, egyszerűen gondolj arra, mit szólnék, ha most nyitnám meg ezt a kódot először? Mi lenne az első benyomásom? Kihasználtam-e minden lehetőséget amiről tudtam, és a rendelkezésemre állt? Ha ezekre a válasz igen, akkor nyugodtan adhatod át.

 

0 válaszok

Hagyjon egy választ

Want to join the discussion?
Feel free to contribute!

Vélemény, hozzászólás?

Az email címet nem tesszük közzé.