View
107
Download
3
Category
Tags:
Preview:
DESCRIPTION
Do you write extensions for Joomla? Do it the *right* way. You will save time and make friends amongst fellow developers (because they won't hate you when they have to read your code). In this session we will share standards and suggestions about the best practices to adopt when you code your extensions. Based on a true story. Our own.
Citation preview
Joomla Extensions Development Best Practices
Francesco Abeni GiBiLogicextensions.gibilogic.com
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
¡Hola, mundo!
Shameless self-promotion
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
FrancescoAbeni
sPrintAddCSSPizzaBox
About this speech
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
The quality of code in the Joomlasphere
Today roadmap:
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● Tools● Files and folders● Reuse software● MVC● Other tips● Conclusions
Feedback please!
No dev course
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Our target
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Good = not bad
Excellent = above the average
Good is enough for today
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
IDE basic features
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● multiple files edit● syntax highlighting● index for methods and variables● autocompletion● autoformatting● compiler● versioning / unit testing / phpdoc / ...
Some IDEs
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Versioning
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
http://git-scm.com/book
Standard
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Everything in its right place
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Backend ● administrator○ components
■ com_componentname● componentname.xml● componentname.php● controllers● models● views
Everything in its right place
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Backend ● administrator○ components
■ com_componentname● ...● config.xml● install.php● sql● tables● helpers
● media○ com_componentname
■ css■ js■ img
● components○ com_componentname
■ componentname.php■ controllers■ models■ views
Everything in its right place
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Frontend
● images○ com_componentname
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
CSS / JS
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Use existing libraries
JavaScript● MooTools (since Joomla 1.5)● JQuery (since Joomla 2.5)
CSS + JavaScript● Bootstrap (since Joomla 3.x)
P.S. got conflicts? Use JQueryEasy plugin.
CSS out of the door
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Don't:<div>...</div><br style="clear: both"><div style="height: 200px">...</div>
Do:<link rel=”stylesheet” href=”/media/componentname/styles.css”>...<div class=”clearfix”>...</div><div class="fixedheight">...</div>
.clearfix { … }
.fixedheight { height: 200px }
JS out of the door
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<button id="submit" type=submit" value="ClickMe!" onclick="validateForm()" />
Do:<script src=”/media/componentname/js/script.js”><button id="submit" type=submit" value="ClickMe!"/>
document.addEvent('load',function(){ $('submit').addEvent('click',function(){
validateForm(); });
});
Don't:
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Joomla framework
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● JApplication● JDatabase● JUser● JSession● JDocument● JHTML● JForm● JConfig● JUri
● JFile● JFolder● JLog● JFilterInput ● JError and JException● JDate● JUtilities● JVersion● JLayout
PHP functions and classes
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● pcre● trim● usort● array_map● array_unique● json_encode● json_decode● microtime(true)● glob● curl
● DateTime● Standard PHP Library● Exception● SimpleXML● TCPDF● PHPMailer
PHP version
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● 16. Dec 2010: PHP 5.2 end of life● 11. Jul 2013: PHP 5.3 end of life● 01. Mar 2012: PHP 5.4 released ● 20 Jun 2013: PHP 5.5 released
● PHP 5.4 is 40% faster than PHP 5.2
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Real objects
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
book writer library
Bad design sample
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● views/search● views/editbook● views/book● views/books● views/booksauthor● views/topten
Don't:
The controller
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● Filters input● Decides what to do● Checks access permissions● Executes task(s)● Optionally, calls the view
The model
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● Retrieves object data● Validates object data● Gets object data● Saves object data● Hates to be mistaken as an helper
The view
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● Ask the model for data● Display object(s)● Uses layouts!
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Header comment
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php
/*** @version mybooks.php 2013-08-10 15:23:00Z zanardi* @package GiBi MyBooks* @author GiBiLogic* @authorUrl http://www.gibilogic.com* @authorEmail info@gibilogic.com* @copyright Copyright (C) 2013 GiBiLogic. All rights reserved.* @license GNU/GPL v2 or later* @description Backend entry point*/
Entry point
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php...defined('_JEXEC') or die();
jimport('joomla.application.component.controller');
$view = JFactory::getApplication()->input->get('view', 'book');$task = JFactory::getApplication()->input->get('task', 'index');JFactory::getApplication()->input->set('task', "$view.$task");
$controller = JController::getInstance('MyBooks');$controller->execute($task);$controller->redirect();
Controller - part 1
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php...defined('_JEXEC') or die('The way is shut!');
jimport('joomla.application.component.controlleradmin');
/*** MyBooksControllerBook class.** @see JControllerAdmin*/class MyBooksControllerBook extends JControllerAdmin{ /** * Controller's view. * * @var JView */ private $view; ...
Controller - part 2
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... /** * Class constructor. * * @param type $config */ public function __construct($config = array()) { parent::__construct($config);
$this->model = $this->getModel();
$this->view = $this->getView(JFactory::getApplication()->input->get('view', 'book'), 'html'); $this->view->setModel($this->model, true); $this->view->setModel($this->getModel('Author', 'MyBooksModel'), false); $this->view->setModel($this->getModel('Editor', 'MyBooksModel'), false); } ...
Controller - part 3
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... public function index() { $this->view->setLayout('index') $this->view->display(); }
public function create() { $this->view->setLayout('create'); $this->view->display(); }
public function save() { $data = JFactory::getApplication()->input->get('jform', null); if (!$data || !$this->model->validate($data)) { $msg = 'Invalid data!'; $type = 'error'; $this->setRedirect('index.php?option=com_mybooks&view=book&task=create', $msg, $type); return false; }
Model - part 1
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php...defined('_JEXEC') or die('The way is shut!');
jimport('joomla.application.component.model');jimport('joomla.html.pagination');
class MybooksModelBook extends JModel{ private $table = '#__mybooks_book';
public function __construct($config = array()) { parent::__construct($config);
$app = JFactory::getApplication(); $limit = $app->getUserStateFromRequest('global.list.limit', 'limit', $app->getCfg('list_limit'), 'int'); $limitstart = $app->input->get('limitstart', 0, '', 'int'); $this->setState('limit', $limit); $this->setState('limitstart', $limitstart); $this->setState('author_id', $app->getUserStateFromRequest('com_mybooks.filters.author_id', 'author_id', 0, 'int')); }
Model - part 2
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... public function getList() { return $this->_getList( $this->buildQuery(), $this->getState('limitstart'), $this->getState('limit') ); }
public function getLast() { $query = $this->_db->getQuery(true); $query->select('*')->from($this->table)->orderby('created_at DESC'); $this->_db->setQuery($query,0,1); $results = $this->_db->loadObjectList('id'); return $results ? $results : array(); }
public function getLastByAuthor($author_id) { $query = $this->_db->getQuery(true); $query->select('*')->from($this->table)->where(“author_id = '$author_id'”)->orderby('created_at DESC'); $this->_db->setQuery($query,0,1); return $this->_db->loadObject(); }
Model - part 3
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... public function create($data) { if (!$data) { return 0; }
$data['created_at'] = date('Y-m-d H:i:s');
$query = $this->_db->getQuery(true);
$query->insert($this->table)->columns(array_keys($data))->values(sprintf("'%s'", implode("','", array_values($data)))); $this->_db->setQuery($query);
return false === $this->_db->execute() ? 0 : $this->_db->insertid(); } ...
Model - part 4
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... public function delete($ids) { $query = $this->_db->getQuery(true); $query->delete()->from($this->table)->where('id IN '.implode(',', $ids)); return false !== $this->_db->execute(); }
public function getPagination(){ return new JPagination( $this->_getListCount($this->buildQuery()), $this->getState('limitstart'), $this->getState('limit') ); }
private function buildQuery(){ $where = $this->buildWhere(); $query = $this->_db->getQuery(true); return $query->select('*')->from($this->table)->where($where)->orderby('created_at DESC'); } ...
View - part 1
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php...defined('_JEXEC') or die('The way is shut!');
jimport('joomla.application.component.view');
class MyBooksViewBook extends JView{ public function display($tpl = null) { $this->pagination = $this->getModel()->getPagination(); $this->filter_author_id = $this->getModel()->getState('author_id');
$this->books = $this->getModel()->findAll(); $this->authors = $this->getModel('Authors')->getList(); $this->editors = $this->getModel('Editors')->getList();
$this->addToolbar($tpl);
parent::display($tpl); }
View - part 2
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php ... protected function addToolbar($tpl){ $methodName = 'addToolBar' . ucfirst(!$tpl ? 'default' : $tpl); $this->{$methodName}(); }
private function addToolBarDefault(){ JToolBarHelper::title(JText::_('COM_MYBOOKS') . ': ' . JText::_('COM_MYBOOKS_BOOK_LIST')); JToolBarHelper::addNew('create'); JToolBarHelper::preferences('com_mybooks'); JToolBarHelper::divider(); JToolBarHelper::deleteList('COM_MYBOOKS_BOOK_LIST_DELETE_CONFIRM', 'delete'); }
private function addToolBarCreate(){ JToolBarHelper::title(JText::_('COM_MYBOOKS') . ': ' . JText::_('COM_MYBOOKS_BOOK_NEW')); JToolBarHelper::apply('save'); JToolBarHelper::divider(); JToolBarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mybooks'); }}
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Helpers
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Common (usually static) functions not related to a specific object
● Get date / time / external info● Format date and numbers● Build title and/or other HTML snippets● Log/error management● Handle CURL connections
Table classes
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Interface to and from the database
Active Record pattern
● Define table name and unique id● load, store, delete, and so on
Table classes - sample code
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
<?php
class TableBook extends JTable { public function __construct(&$db) { parent::__construct(‘#__books’, ‘id’, $db); } }
Layouts
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Page types related to a single view (object)"tmpl" subfolder (template override)
● List● Single item (readonly)● Single item (edit form)● Blog● ...
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
System messages
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
$app = JFactory::getApplication();$app->enqueueMessage( $msg, $type )
JError / JException / JLog
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Error handling vs. logging
● JError is deprecated● JException is deprecated● Use PHP Exception class(es)
● JLog is a way to track what's happening
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Visibility
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Variables and methods
● "var ..." is deprecated since PHP 5.1.2● public : available from other classes● private : available only from the class● protected : available from the class and
from inherited or parent classes
Constants
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● constants instead of variables● drop DS● drop DIRECTORY_SEPARATOR● use Joomla constants:
http://docs.joomla.org/Constants● warning: JPATH_SITE vs JPATH_BASE vs
JPATH_ROOT vs JPATH_ADMINISTRATOR
Versioning
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
● version format: major.minor.release (es. v3.1.5)● variant: v3.1.5 Free, v3.1.5 Pro
And the road goes on and on
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Thanks :)
f.abeni@gibilogic.com@f_abeni / @gibilogic
http://www.slideshare.net/FrancescoAbeni/best-practices-for-joomla-extensions-developers-25353320
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - info@gibilogic.com
Feedback please!
Recommended