Magento (1.9) EAV a gyakorlatban (frontend)

A Magento alatt ez a következőképpen néz ki: 

  • entity tábla (meghatározza az entity-t)
  • attribute tábla (meghatározza a tulajdonságot, annak típusát, egyéb opciókat, pl.: source model, type, input, label)
  • value tábla (ez valójában típusonként szét van választva, többek között az indexelhetőség, keresés miatt pl.: catalog_product_entity_int, catalog_product_entity_varchar stb.)

A tulajdonságokat tárolhatnánk a fő-táblában is, nem?

Az EAV adatmodellben az elemekhez való új tulajdonságok felvitele során nem kell az eredeti entity táblát módosítani, így egyrészt elkerülhetők a túl nagy táblák, ezzel a lekérdezések sebessége is gyorsabb lesz, másrészt pedig a listáknál a felesleges adatok nem jelennek meg, csak amelyeket mi hozzáadunk a collection-höz. A cikk során egy modult fogunk készíteni, admin és frontend felülettel, EAV megoldással.

Miután a webáruházunkat feltelepítettük a szerverre, és konfiguráltuk azt (base url, database name, database user stb.), minden megjelenik rendeltetésszerűen, elkezdjük a fejlesztést.

Modulunk definilásához, az app/etc/modules könyvtárban kell létrehozni egy a module nevével megjelölt xml fájlt. Az én esetemben ez az Aion_Items.xml, a codePool pedig local, de a community codePool-ban is elhelyezhetnénk.

Az xml a következőképpen néz ki:

 

<?xml version="1.0"?>
<config>
    <modules>
        <Aion_Items>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Core/>
            </depends>
        </Aion_Items>
    </modules>
</config>

 

Ez még nem elég, a module még cache-ürítés után sem fog megjelenni, az admin-felület modul-listájában sem. A következő lépés, hogy az xml-ben definiált modult, a codePool-ban megadott könyvtárban létrehozzuk. Ez ebben az esetben, az app/code mappában jelent egy local könyvtárat (amennyiben még nem létezik), azon belül egy Aion/Items mappaszerkezetet. Tehát a végső elérésünk ebben az esetben app/code/local/Aion/Items.

module_path

 

A következő lépésekben a fő irányadók az általános modulkészítés szabályai, pár eltéréssel. Ha nem jut eszedbe éppen valami, nem szégyen puskázni a Magento core modulok tartalmából :)

Hozzuk létre a modul config.xml fájlt. Haladjunk lépésről lépésre:

 

<?xml version="1.0" encoding="UTF-8" ?>
<config>
    <modules>
        <Aion_Items>
            <version>0.0.1</version>
        </Aion_Items>
    </modules>
</config>

 

Definiáljuk a modellek, resource modellek elérhetőségét, illetve az entity táblá(ka)t a modulunknak. Folytatólagosan a </config> node elé helyezzük el a következőket, közvetlenül a </modules> node után:

 

<global>
    <models>
        <aion_items>
            <class>Aion_Items_Model</class>
            <resourceModel>aion_items_resource</resourceModel>
        </aion_items>
        <aion_items_resource>
            <class>Aion_Items_Model_Resource</class>
            <entities>
                <article>
                    <table>aion_items_article</table>
                </article>

 

Nem zártuk még le az <entities> node-ot, továbbra is ezen belül maradunk, és meghatározzuk a többi, EAV adattáblát, típusonként:

 

            <article_datetime>
                <table>aion_items_article_datetime</table>
            </article_datetime>
            <article_decimal>
                <table>aion_items_article_decimal</table>
            </article_decimal>
            <article_int>
                <table>aion_items_article_int</table>
            </article_int>
            <article_text>
                <table>aion_items_article_text</table>
            </article_text>
            <article_varchar>
                <table>aion_items_article_varchar</table>
            </article_varchar>
            <article_char>
                <table>aion_items_article_char</table>
            </article_char>
        </entities>
    </aion_items_resource>
</models>

 

Majd pedig a helper-ek, block-ok elérése, definiálása következik:

 

<helpers>
    <aion_items>
        <class>Aion_Items_Helper</class>
    </aion_items>
</helpers>
<blocks>
    <aion_items>
        <class>Aion_Items_Block</class>
    </aion_items>
</blocks>

 

Most, hogy a tábláink létre is jöjjenek, szükség lesz az erőforrások megadására, ami az installer és adatbázis-kapcsolat meghatározását jelenti, folytatólagosan a </blocks> alá tehetjük, de az számít, hogy ezek egy szinten legyenek.

 

        <resources>
            <aion_items_setup>
                <setup>
                    <module>Aion_Items</module>
                    <class>Aion_Items_Model_Resource_Setup</class>
                </setup>
            </aion_items_setup>
        </resources>
    </global>
</config>

 

Láthatóan le is zártam a node-okat, a következő lépések az alap helper (ez nagyon fontos(!), e nélkül nem fog működni a modulunk), majd a modellek létrehozása lesz. Az xml-ben megadottak szerinti helyen kell ezeket létrehozni. A modelleket tehát az Aion/Items/Model, a helper-t pedig az Aion/Items/Helper mappában.

A modulhoz tartozó default helper a Data nevet viseli minden esetben, a

Mage::getHelper(‘aion_items’) ezt próbálja meg példányosítani amely egyenértékű a Mage::getHelper(‘aion_items/data’) hívással.

Helperünk tehát az Aion_Items_Helper_Data osztálynevet, Data fájlnevet viseli, és a Mage_Core_Helper_Abstract osztályból származtatjuk.

Hozzuk létre a modelleket, és resource modelleket!

Az <entities> alatt az article-t határoztuk meg, így legyen a modellünk is ez, hozzuk létre az

  • Aion_Items_Model_Article
  • Aion_Items_Model_Resource_Article
  • Aion_Items_Model_Resource_Article_Collection osztályokat.

Mivel EAV adatmodell mellett döntöttünk, így nem a szokásos Mage_Core_Model_Resource_Abstract, és Mage_Core_Model_Resource_Db_Collection_Abstract osztályokból származtatjuk ezeket, hanem az eddigiek sorrendjében a következő osztályokból:

  • Mage_Eav_Model_Entity_Abstract
  • Mage_Eav_Model_Entity_Collection_Abstract

A resource model-ben van még egy kis változás:

 

class Aion_Items_Model_Resource_Article extends Mage_Eav_Model_Entity_Abstract
{
const ENTITY = ‘aion_items_article’;

public function __construct()
{
$this->setType(self::ENTITY)
->setConnection(‘core_read’, ‘core_write’);
}
}

 

Nem véletlenül maradt ki a felsorolásból az első modell (Aion_Items_Model_Article). Ezt továbbra is a Mage_Core_Model_Abstract osztályból származtatjuk, nincs változás. Ha megvannak a modellek, még az installer osztályt kell létrehozni. Ezt már szintén megadtuk a modul alap-konfigurációs fájljában (Aion_Items_Model_Resource_Setup). Hozzuk létre a megfelelő helyen, tartalma pedig legyen a következő:

 

class Aion_Items_Model_Resource_Setup extends Mage_Eav_Model_Entity_Setup
{
    public function getDefaultEntities()
    {
        $entityAttributes = array(
            Aion_Items_Model_Resource_Article::ENTITY => array(
                'entity_model' => 'aion_items/article',
                'attribute_model' => '',
                'table' => 'aion_items/article',
                'attributes' => array(
                    'name' => array(
                        'type' => 'varchar',
                        'label' => 'Name',
                        'input' => 'text',
                        'global' => 0,
                        //this set the attribute to store entity data separated to every store not globally
                        'visible' => true,
                        'required' => true,
                        'user_defined' => true,
                        'visible_on_front' => true
                    ),
                    'customer_id' => array(
                        'type' => 'integer',
                        'label' => 'Customer',
                        'input' => 'select',
                        'global' => 1,
                        'visible' => false,
                        'required' => true,
                        'user_defined' => true,
                        'visible_on_front' => false,
                        'source' => 'aion_items/article_attribute_source_customer'
                    ),
                ),
            )
        );

        return $entityAttributes;
    }
}

 

Ezeket a tulajdonságokat fogja létrehozni a telepítő lefutáskor az entity típushoz. A fenti tulajdonság opciókon felül, természetesen van más lehetőség is, de mivel ezek jelen esetben nem térnek el default értékeitől, nem szükséges megadni.

A customer_id tulajdonságnál látható, hogy megadtuk a source opciót, így ehhez szükségünk lesz egy source modellre. Hozzuk létre tehát a megadott helyen és néven, az app/code/local/Aion/Items/Model/Article/Attribute/Source mappában a Customer.php fájlt, tartalma pedig legyen a következő:

 

class Aion_Items_Model_Article_Attribute_Source_Customer extends Mage_Eav_Model_Entity_Attribute_Source_Table
{
    public function getAllOptions()
    {
        if (!$this->_options) {
            $customers = Mage::getResourceModel('customer/customer_collection')
                ->addAttributeToSort('lastname', 'ASC')
                ->addAttributeToSort('firstname', 'ASC');
            foreach ($customers as $customer) {
                $name = [$customer->getFirstname(), $customer->getLastname()];
                $this->_options[] = ['value' => $customer->getId(), 'label' => join(' ', $name)];
            }
        }
        return $this->_options;
    }
}

 

Ahhoz, hogy ezek után létrehozzuk a táblánkat, már csupán az installerünk kell létrehozni. Ezt a saját modulunk alatti sql, azon belül pedig a <resources> node alatt definiált mappában kell megtenni. Mivel ebben az esetben ez az <aion_items_setup>, így a mappa is  aion_items_setup lesz.

A modulunk config.xml fájljában korábban megadtuk a verziót, <version> node-ban, ez lesz az installer-ünk verziószáma. Hozzunk létre tehát egy install-0.0.1.php fájlt az app/code/local/Aion/Items/sql/aion_items_setup mappában.

Ennek a tartalma a következő:

 

try {
    $this->startSetup();
    $this->createEntityTables('aion_items/article');
    $this->addEntityType(Aion_Items_Model_Resource_Article::ENTITY, [
        'entity_model' => 'aion_items/article',
        'attribute_model' => '',
        'table' => 'aion_items/article',
        'increment_model' => '',
        'increment_per_store' => '0'
    ]);

    $this->installEntities();
    $this->endSetup();
} catch (Exception $e) {
    Mage::logException($e);
}

 

Ha ezzel megvagyunk, készen áll a modul a telepítésre. Mielőtt azonban lefuttatnánk, nézzük meg a mappa- és fájlszerkezetünket, mely a következőképpen néz ki:

 

files_in_module_1

 

 

Hogy modulunk installer-e lefusson, az admin felületen System -> Cache management menüpont alatt, ürítsük a cache-t. Amennyiben sikeresen lefutott az installer, a következő táblákat kell látnunk az alap táblákon felül az adatbáziskban.

 

eav_tables

 

 

Hogy biztosra menjünk, kapcsoljuk be a logolást az admin-felületen az installer futtatása előtt, illetve utána nézzük meg a var/log mappát, exception.log vagy system.log fájlok találhatóak-e benne. Ezenfelül az eav_attribute táblában az attribute_id szerint csökkenőbe rendezve láthatjuk, hogy a customer_id, és name jellemzőink létrejöttek-e, illetve mellette az entity_id mezőben láthatjuk, hányas ID-t kapta az új entity típusunk. Rákattintva meg is tekinthetjük azt.

Ha eddig minden rendben zajlott, csináljunk egy frontend oldalt a modulunknak, hogy letesztelhessük, minden helyesen működik-e. A <global> node alatt, de még a </config> node előtt helyezzük el a következő kód-blokkot:

 

<frontend>
    <routers>
        <aion_items>
            <use>standard</use>
            <args>
                <module>Aion_Items</module>
                <frontName>aionitems</frontName>
            </args>
        </aion_items>
    </routers>
</frontend>

 

A frontend oldali megjelenítéshez szükségünk van még layout, controller, block, és template fájlra. Ugyan ezen <frontend> node alá helyezzük el a layout fájl definiálását:

 

<layout>
    <updates>
        <aion_items>
            <file>aion/items.xml</file>
        </aion_items>
    </updates>
</layout>

 

Ezek után a fájlt hozzuk létre annak helyén (app/design/frontend/rwd/default/layout). Fontos, hogy a modulunkhoz tartozó alap template fájlokat mindig a témához tartozó default alatti layout könyvtárban hozzuk létre, hogy a későbbi módosításkor annak másolatán, ne pedig az eredeti fájlon hajtsanak végre módosításokat. Ugyanez érvényes a template fájlokra is.

 

<?xml version="1.0"?>
<layout version="0.1.0">
    <aion_items_index_index translate="label">
        <label>Aion Items</label>
        <reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
        </reference>
        <reference name="content">
            <block type="aion_items/list" name="aion.items.list" as="aion_items_list" template="aion/items/index.phtml"/>
        </reference>
    </aion_items_index_index>
</layout>

 

Ha ez is megvan, jön a template fájl és a block. Template fájlnak az xml-ben az aion/items/index.phtml-t jelöltük meg, így egy szinttel fentebb, az app/design/frontend/rwd/default/template/aion/items mappában helyezzük el az index.phtml fájlt, majd az app/code/local/Acion/Items/Block könyvtárban helyezzünk el egy List.php fájlt amelynek tartalma a következő legyen:

 

class Aion_Items_Block_List extends Mage_Core_Block_Template
{
    /**
     * @return \Aion_Items_Model_Resource_Article_Collection
     * @throws \Mage_Core_Exception
     */
    public function getArticleCollection()
    {
        $articles = Mage::getModel('aion_items/article')->getCollection()
            ->addAttributeToSelect('*');

        return $articles;
    }
}

 

Most térjünk vissza az index.phtml template fájlra, és egyelőre legyen a következő a tartalma:

/** @var Aion_Items_Block_List $this */
$articles = $this->getArticleCollection();
Zend_Debug::dump($articles->getData(),'articles');

 

Hogy megjelenjen a collection tartalma, már csak egy controller-re van szükségünk, hozzuk létre az IndexController.php fájlt az app/code/local/Aion/Items/controllers mappában, az alábbi tartalommal:

 

class Aion_Items_IndexController extends Mage_Core_Controller_Front_Action
{
    /**
     * @return $this
     */
    public function indexAction()
    {
        $this->loadLayout();
        $this->renderLayout();

        return $this;
    }
}

 

Ezután ismét ürítsük a cache-t az admin-felületről, és a frontenden hívjuk meg a modulunkhoz tartozó url-t (a példabeli esetben ez a /aionitems/ ami egyenértékű a /aionitems/index/index/ hívással). Ha minden jól ment, akkor valami hasonlót kell látnod:

 

frontend_display

 

 

Ez eddig szuper, de semmi sem jelenik meg lényegében, hiszen nincs semmilyen adat a táblában. Hogy szemléletesebb legyen a példa, írjunk még egy data-installer-t, hogy láthassuk, működnek-e a jellemzőink is megfelelően.

Az egyik előző képen látható volt egy data mappa, ennek a mappának pontosan ez a szerepe, a nem rendszer-szintű módosításokat ezeken keresztül tudjuk megoldani. Szerkezetben az sql könyvtárhoz kapcsolódik, így ezen belül is szükség van egy aion_items_setup almappára.

A „sima” installer-script-hez képest még annyi eltérés van a működésben, hogy az ezen struktúra alatt lévő fájlokat a Magento egy data- előtaggal keresi.

Tehát az app/code/local/Aion/Items/data/aion_items_setup mappában hozzunk létre egy (mivel már az install-0.0.1.php lefutott) data-upgrade-0.0.1-0.0.2.php nevű fájlt, és az app/code/local/Aion/Items/sql/ mappában szintén egy upgrade-0.0.1-0.0.2.php nevű fájlt. Ez utóbbiban csak megjelöljük, mi történik és hol. Tehát az upgrade-0.0.1-0.0.2.php a következőt tartalmazza:

 

/** @var Aion_Items_Model_Resource_Setup $this */
try {
    $this->startSetup();
    //app\code\local\Aion\Items\data\aion_items_setup\data-upgrade-0.0.1-0.0.2.php
    $this->endSetup();
} catch (Exception $e) {
    Mage::logException($e);
}

 

Az app/code/local/Aion/Items/data/aion_items_setup/data-upgrade-0.0.1-0.0.2.php pedig a következőket tartalmazza:

 

/** @var Aion_Items_Model_Resource_Setup $this */
try {
    $defaultStoreId = Mage::app()->getDefaultStoreView()->getId();
    $attributeSet = Mage::getResourceModel('eav/entity_attribute_set_collection')
        ->addFieldToFilter(
            'entity_type_id',
            Mage::getModel('eav/entity')
                ->setType(Aion_Items_Model_Resource_Article::ENTITY)
                ->getTypeId()
        )->addFieldToFilter(
            'attribute_set_name',
            'Default'
        )->getFirstItem();
    $articles = [
        [
            'store_id' => $defaultStoreId,
            'name' => 'Article name 1',
            'attribute_set_id' => $attributeSet->getId(),
        ],
        [
            'store_id' => $defaultStoreId,
            'name' => 'Article name 2',
            'attribute_set_id' => $attributeSet->getId(),
        ]
    ];
    foreach ($articles as $article) {
        Mage::getModel('aion_items/article')->setData($article)->save();
    }
} catch (Exception $e) {
    Mage::logException($e);
}

 

Ez még nem elég. Ha frissítünk, nem fut le az új installer, de már le sem kell írnom, hogy a cache-t ürítsük :) Ezenfelül pedig ugye még a modulhoz tartozó config.xml fájlban a <version> node alatt szereplő 0.0.1-et növeljük 0.0.2-re, majd frissítsük az oldalt. Ha minden rendben ment (és reméljük, hogy igen), akkor a következőt kell látnod az adatbázisban:

 

data_installed

 

 

Az aion_items_article_varchar táblában pedig külön szerepelnek a nevek az entitásokhoz tartozóan.

 

data_eav_installed

 

Ha minden rendben ment, a frontenden a frissítést követően a következőt láthatjuk:

 

frontend_display_data

 

Összegzés

Azt hiszem, nem mondok nagy butaságot ha kijelentem, hogy az EAV egy nagyon hasznos ugyanakkor  komplex dolog, amire az esetek nagy részében nincs szükség. A többnyire kis, illetve közepes fejlesztések ‒ mivel általában eleve több, egymáshoz kapcsolódó tábláról van szó ‒ nem indokolják ezt a szerkezeti felépítést.

Érdemes azonban ezt már az elején lefektetni, mivel ‒ ahogy a Magento maga is teszi saját ezen struktúrában tárolt adataival ‒ ezeket lehet egy generált, a szükséges adatokat megjelenítő összesítő táblában tárolni (flat). Igen időigényes és költséges lenne egy már meglévő, nagy számú komoly táblaszerkezetet EAV struktúrába átültetni.

 

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é.