sobipro是joomla上的CCK组件,之所以会有这样的组件是因为joomla并没有为内容添加字段的功能,joomla虽然是CMS,但它的内容组件可定制性十分弱,内容表现性并不丰富,所以不得不依赖第三方CCK组件。而sobipro是joomla cck组件中算得上比较专业的一款。
说它专业,除了它有强大的内容管理功能之外还有可扩展性,可以根据实现需要购买与下载官方提供的扩展模块,代码质量与稳健性也不错。但我在对它进行二次开发时发现了不少问题,往往会导致开发效率低下。以下是我总结的几点。
joomla由于joomla1.5至joomla2.5过渡框架差异比较大,而且joomla框架提供的架构也不够灵活,所以不少组件会在joomla之上再建造兼容框架,为了可以同时兼容joomla新旧版本。而sobipro虽然也是MVC结构,但与joomla框架差异很大如果官方没有提供开发文档,只凭自己阅读代码相差花时间。
├─base │ └─fs ├─cms (joomla兼容层) │ ├─joomla15 │ │ ├─base │ │ └─html │ ├─joomla16 │ │ ├─base │ │ ├─elements │ │ └─html │ └─joomla_common │ ├─base │ ├─elements │ └─html ├─ctrl (控制器) │ └─adm ├─env ├─helpers (辅助) │ └─adm ├─js │ ├─adm │ ├─calendar │ │ └─lang │ └─codemirror ├─mlo ├─models (模型) │ ├─adm │ └─fields ├─plugins (插件) ├─services │ └─installers │ └─schemas ├─types └─views (视图) └─adm
我第一次使用xslt就是在sobipro上,才知道xslt有多么难用,语法冗长、模板不灵活、调试难度大等问题严重影响开发效率。假设你只需要小修改它的模板,你必须先学习xslt语法,以下是我做一个动态链接的代码
<a class="field_positon_ordering" href="{$url}&ordering=field_position.asc"> <xsl:if test="contains($ordering_classes, 'field_position') and contains($ordering_classes, 'asc')"> <xsl:attribute name="href"> <xsl:value-of select="concat($url, '&ordering=field_position.desc')" /> </xsl:attribute> </xsl:if> Position <small class="arrow"></small> </a>
这仅仅只是改模板,如果你需要添加一些数据属性,而XML并没有输出,那你只能修改view层给它添加数据,以下是sobipro输出XML之前的部分代码
$data[ 'name' ] = array( '_complex' => 1, '_data' => $this->get( 'listing_name' ), '_attributes' => array( 'lang' => Sobi::Lang( false ) ) ); if( Sobi::Cfg( 'category.show_desc' ) ) { $desc = $current->get( 'description' ); if( Sobi::Cfg( 'category.parse_desc' ) ) { Sobi::Trigger( 'prepare', 'Content', array( &$desc, $current ) ); } $data[ 'description' ] = array( '_complex' => 1, '_cdata' => 1, '_data' => $desc, '_attributes' => array( 'lang' => Sobi::Lang( false ) ) ); } $data[ 'meta' ] = array( 'description' => $current->get( 'metaDesc' ), 'keys' => $this->metaKeys( $current ), 'author' => $current->get( 'metaAuthor' ), 'robots' => $current->get( 'metaRobots' ), ); $data[ 'entries_in_line' ] = $this->get( '$eInLine' ); $data[ 'categories_in_line' ] = $this->get( '$cInLine' );
这只是很小的一部分,其它简直是不知所云。如果官方有文档提供,这里的数据属性应该会比较好理解。
只是这种难度可能对于大多数人来是可以接受的,但你还得先找到你所见页面的view层在哪里,因为它真的很难找。joomla一般可以通过URL找到对应代码
index.php?option=com_content&view=article&id=1788
以上地址很容易可以知道代码在components/com_content/views/article/view.html.php但sobipro并不总是这样,例如这个地址
index.php?option=com_sobipro&sid=5980
这个页面是分类列表页?分类页?内容页?都可能是!sobipro把三大数据类型section, category, content都叫entity,并且放在同一个数据表中,而且都用sid作为主键。所以你给它一个sid,它先在数据库查这个entity是什么类型,如果是category,就会给你category的Controller, Model, View, template。而因为这三大类型具有许多相同属性,所以它们的MVC是会放上继承的。例如category的view层其实是使用了section的view层,找起来像面条似的,你不做一些标记很容易会漏掉。幸好它只有这三种层次,所以我给所有的view层都加debug代码还是能找出来的,只是debug信息它都会屏敝。也由于输出是XML再用xslt转为HTML,像var_dump这样粗暴的方法会直接破坏xml代码,让xslt转换失败直接给你反馈空白页。sobipro改错一点就出现空白页,所以很难知道是什么原因导致的。
joomla2开始已经有CRUD可用了,不像joomla 1.5需要写SQL。但如果要兼容新旧版本就可能需要再造CRUD的轮子。但造轮子也不至于这么难看吧
$table = $db->join( array( array( 'table' => 'spdb_field_option_selected', 'as' => 'opts' ), array( 'table' => 'spdb_language', 'as' => 'lang', 'key' => array( 'opts.optValue', 'lang.sKey' ) ), array( 'table' => 'spdb_object', 'as' => 'spo', 'key' => array( 'opts.sid', 'spo.id' ) ), array( 'table' => 'spdb_relations', 'as' => 'sprl', 'key' => array( 'opts.sid', 'sprl.id' ) ), ) ); try { $db->select( $oPrefix . 'id', $table, $conditions, $eOrder, $eLimit, $eLimStart, true ); $results = $db->loadResultArray(); } catch ( SPException $x ) { Sobi::Error( 'AlphaListing', SPLang::e( 'DB_REPORTS_ERR', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); }
这样的数据操作代码比SQL丑太多了,填一堆array的出错率高得很。不喜欢它可以使用joomla原生的查询方法,但二次开发情况有复杂,直接修改它会比重新写要稳定得多。特别是很多时候你不得不修改插件。
一般C层作为入口都代表页面或者功能入口,但sobipro而数据类型为入口,我真不知道这种层次有什么优点,但缺点倒是不少。例如alpha插件(字母列表)只不过在原来的分类列表页加上字母筛选,用的都是同一套template,但alpha的M层竟然需要仿照分类页的M层重写一次,也就是我如果在列表页上添加新功能,我就需要在alpha插件再加一次。那是因为alpha不能复用M层,不得不在C层写SQL代码,并且还是很近似的代码,这样做成MVC有何用?
sobipro如此美丽又专业的外观,却隐藏如此不合理的设计,但这在joomla的扩展群里是个普遍现象,好看不好使,好使不好改,sobipro也已经是其中做得不错的一个组件了,建议在做joomla二次开发时最好不要做太复杂的功能,也许简单的修改你都有可能改到吐血。