MagentoのDoctrine I18nのアイデア

すべては、私が別のフリーランスプロジェクトの改訂に着手したという事実から始まりました。 タスクは、ギャラリーの多言語モジュールの実装を完了することでした。 いつも突然、問題が発生しました...



進歩のエンジン



ギャラリーはアルバムとアイテムで構成されています。 アルバムとアイテムの両方には、次のメインフィールドがあります。



タイトルと説明は、StoreView、つまりデータベース内のstore_idに依存する必要があることがわかります。 最初は別のstore_idフィールドを追加するオプションが最初にありましたが、最初に行ったように、このソリューションは単純にばかげていることがわかりました! 理由を説明します。



悪くはないようですが、エンドユーザー向けではありません。



フリークアイデア



私は最近Symfony1.4 + Doctrine1.2をよく使うので、長い間解決策を探す必要はありませんでした。 Doctrine I18nの動作に似た機能をMagentoに実装することにしました。



なんで?



しかし、それは便利でシンプルだからです! さらに、これを実装する標準機能が見つかりませんでした。 EAVを介してすべてを実行するというアイデアがありましたが(entity_type、属性、およびそこに必要な他のすべてを作成します)、私にとっては-複雑でわかりにくいです。

このソリューションの魅力は、同じで外部APIに残っているコレクションとモデルですが、複数のStoreViewのデータを保存および共有することを考慮する必要がないことです。



安くて陽気な



実装自体は、Sourceモデル(コレクションとモデル自体)の抽象化の別のレイヤーを作成することです。 そして、複数のStoreViewのデータの保存と操作を実装する必要がある場合、それらから単純に継承します。



クラス自体は次のとおりです。

-アプリ/コード/ローカル/ Sj /ギャラリー/モデル/ Mysql4 / Translation.php

abstract class Sj_Gallery_Model_Mysql4_Translation extends Mage_Core_Model_Mysql4_Abstract

{

const TABLE_SUFIX = '_translation' ;

protected

$_translatableFields = array();



/**

* Standard resource model initialization

*

* @param string $mainTable

* @param string $idFieldName

* @return Mage_Core_Model_Mysql4_Abstract

*/

protected function _init($mainTable, $idFieldName)

{

if (empty($ this ->_translatableFields)) {

throw new Exception( 'You must specify translatable fields' );

}

$ this ->_setMainTable($mainTable, $idFieldName);

}



/**

* Retrieve select object for load object data

*

* @param string $field

* @param mixed $value

* @return Zend_Db_Select

*/

protected function _getLoadSelect($field, $value, $ object )

{

$tableName = $ this ->getMainTable();

$select = parent::_getLoadSelect($field, $value, $ object );



$select->joinLeft(

array( 'trnslt' => $ this ->getTranslationTableName()),

'trnslt.id = ' . $tableName . '.' . $field . '

AND trnslt.store_id = '
. ( int )$ object ->getStoreId(),

$ this ->getTranslatableColumns()

);



return $select;

}



/**

* Set multilang field names

*

* @param array $fields

* @return Sj_Gallery_Model_Mysql4_Translation

*/

public function setTranslatableFields($fields)

{

if (!is_array($fields)) {

return false ;

}



$ this ->_translatableFields = $fields;

return $ this ;

}



/**

* Get multilang field names

*

* @return array

*/

public function getTranslatableFields()

{

return $ this ->_translatableFields;

}



/**

* Get multilang columns

*

* @return array

*/

public function getTranslatableColumns()

{

$columns = $ this ->getTranslatableFields();

$columns[ 'translation_id' ] = 'trnslt.id' ;

$columns[ 'store_id' ] = 'trnslt.store_id' ;

return $columns;

}



/**

* Get translation table name

*

* @return string

*/

public function getTranslationTableName()

{

return $ this ->getMainTable() . self::TABLE_SUFIX;

}



/**

* Save object object data

*

* @param Mage_Core_Model_Abstract $object

* @return Mage_Core_Model_Mysql4_Abstract

*/

public function save(Mage_Core_Model_Abstract $ object )

{

$adapter = $ this ->_getWriteAdapter();

$adapter->beginTransaction();

try {

$data = $ object ->getData();

$translations = array();

foreach ($ this ->_translatableFields as $field) {

if (isset($data[$field])) {

$translations[$field] = $data[$field];

unset($data[$field]);

}

}

$onDuplicate = array_keys($translations);

$translations[ 'id' ] = $ object ->getId();

$translations[ 'store_id' ] = $ object ->getStoreId();



$adapter->insertOnDuplicate(

$ this ->getTranslationTableName(),

$translations,

array_combine($onDuplicate, $onDuplicate)

);

parent::save($ object );



$adapter->commit();

return $ this ;

} catch (Exception $e) {

$adapter->rollBack();

throw $e;

}

}

}








*このソースコードは、 ソースコードハイライターで強調表示されました。




-アプリ/コード/ローカル/ Sj /ギャラリー/モデル/ Mysql4 /翻訳/ Collection.php

abstract class Sj_Gallery_Model_Mysql4_Translation_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract

{

protected function _initSelect()

{

$tableName = $ this ->getResource()->getMainTable();



$ this ->getSelect()

->from(array( 'main_table' => $tableName))

->joinLeft(array( 'trnslt' => $ this ->getResource()->getTranslationTableName()),

'trnslt.id = main_table.' . $ this ->getResource()->getIdFieldName(),

$ this ->getResource()->getTranslatableColumns()

);

return $ this ;

}



public function addStoreToFilter(Mage_Core_Model_Store $store)

{

$ this ->addFieldToFilter( 'trnslt.store_id' , $store->getId());

return $ this ;

}

}







*このソースコードは、 ソースコードハイライターで強調表示されました。




練習する



すべてが非常に単純です。 以前は、ソースモデルを作成するとき、Sj_Gallery_Model_Mysql4_TranslationからMage_Core_Model_Mysql4_Abstractを継承する必要がありました。

また、モジュールのインストールファイルに翻訳用のテーブルを作成する必要があります。 1つのテーブルは、「_ translation」の別のサフィックスです(この値はクラス定数であり、変更できます)。

唯一かつ非常に重要な点は、loadメソッドを呼び出す前に、モデルにstore_idを常に設定する必要があるということです!



コレクションの使用例:

$collection = Mage::getModel( 'gallery/group' )->getCollection()

->addStoreToFilter(Mage::app()->getStore())

->addFieldToFilter( 'status' , 1)

->getItems();




* This source code was highlighted with Source Code Highlighter .








モデルの使用例:

$store = Mage::app()->getStore($request->getParam( 'store' ));

$group = Mage::getModel( 'gallery/group' )

->setStoreId($store->getId())

->load($id);




* This source code was highlighted with Source Code Highlighter .








インストールモジュールファイルギャラリーのソースコード:

$installer = $ this ;

$installer->startSetup();



$installer->run( "

CREATE TABLE {$this->getTable('gallery/gallery')} (

`gallery_id` int(11) unsigned NOT NULL AUTO_INCREMENT,

`filename` varchar(255) NOT NULL DEFAULT '',

`status` smallint(6) NOT NULL DEFAULT '0',

`created_time` datetime DEFAULT NULL,

`update_time` datetime DEFAULT NULL,

PRIMARY KEY (`gallery_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

"
);



$installer->run( "

CREATE TABLE IF NOT EXISTS `{$this->getTable('gallery/group')}` (

`collection_id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`file` varchar(255) NOT NULL DEFAULT '',

`status` tinyint(4) NOT NULL,

`created_time` datetime DEFAULT NULL,

`update_time` datetime DEFAULT NULL,

PRIMARY KEY (`collection_id`),

KEY `gallery_group_idx` ( `collection_id` )

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

"
);



$installer->run( "

CREATE TABLE `{$this->getTable('gallery/items_translation')}` (

`id` int(10) unsigned NOT NULL,

`title` varchar(255) NOT NULL DEFAULT '',

`description` varchar(20000) NOT NULL DEFAULT '',

`store_id` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`, `store_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

ALTER TABLE `{$this->getTable('gallery/items_translation')}`

ADD FOREIGN KEY (`id`) REFERENCES `{$this->getTable('gallery/gallery')}` (`gallery_id`)

ON DELETE CASCADE;



CREATE TABLE `{$this->getTable('gallery/group_translation')}` (

`id` int(10) unsigned NOT NULL,

`title` varchar(255) NOT NULL DEFAULT '',

`description` varchar(20000) NOT NULL DEFAULT '',

`store_id` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`, `store_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

ALTER TABLE `{$this->getTable('gallery/group_translation')}`

ADD FOREIGN KEY (`id`) REFERENCES `{$this->getTable('gallery/group')}` (`collection_id`)

ON DELETE CASCADE;

"
);



$installer->endSetup();




* This source code was highlighted with Source Code Highlighter .








ソースはここからダウンロードできます



PS:本格的な国際化機能を実装するという目標を設定しませんでした。 私は問題を解決したばかりで、移植性があり理解しやすいので解決策が気に入りました。 これは、マルチストアビューで透過的に動作するのに十分ですが、さらに改善することができます。 たとえば、すべてをモジュールの形で整理し、セットアップモデルを作成します。セットアップモデル自体が追加のテーブルを作成し、ストアに依存するフィールドの名前を構成に追加します。



この記事では、 ソースコードハイライターを使用してコードを強調表示しました。



All Articles