Best practices for Joomla extensions developers - Joomla Day 2013
-
Upload
francesco-abeni -
Category
Technology
-
view
107 -
download
3
description
Transcript of Best practices for Joomla extensions developers - Joomla Day 2013
Joomla Extensions Development Best Practices
Francesco Abeni GiBiLogicextensions.gibilogic.com
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
¡Hola, mundo!
Shameless self-promotion
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
FrancescoAbeni
sPrintAddCSSPizzaBox
About this speech
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
The quality of code in the Joomlasphere
Today roadmap:
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
● Tools● Files and folders● Reuse software● MVC● Other tips● Conclusions
Feedback please!
No dev course
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Our target
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Good = not bad
Excellent = above the average
Good is enough for today
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
IDE basic features
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
● 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 - [email protected]
Versioning
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
http://git-scm.com/book
Standard
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Everything in its right place
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Backend ● administrator○ components
■ com_componentname● componentname.xml● componentname.php● controllers● models● views
Everything in its right place
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
Frontend
● images○ com_componentname
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
CSS / JS
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
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 - [email protected]
<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 - [email protected]
Joomla framework
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
● 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 - [email protected]
● 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 - [email protected]
● 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 - [email protected]
Real objects
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
book writer library
Bad design sample
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
● views/search● views/editbook● views/book● views/books● views/booksauthor● views/topten
Don't:
The controller
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
● 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 - [email protected]
● 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 - [email protected]
● Ask the model for data● Display object(s)● Uses layouts!
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Header comment
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
<?php
/*** @version mybooks.php 2013-08-10 15:23:00Z zanardi* @package GiBi MyBooks* @author GiBiLogic* @authorUrl http://www.gibilogic.com* @authorEmail [email protected]* @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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
<?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 - [email protected]
Helpers
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
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 - [email protected]
<?php
class TableBook extends JTable { public function __construct(&$db) { parent::__construct(‘#__books’, ‘id’, $db); } }
Layouts
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
System messages
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
$app = JFactory::getApplication();$app->enqueueMessage( $msg, $type )
JError / JException / JLog
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
Visibility
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
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 - [email protected]
● 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 - [email protected]
● 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 - [email protected]
Thanks :)
[email protected]@f_abeni / @gibilogic
http://www.slideshare.net/FrancescoAbeni/best-practices-for-joomla-extensions-developers-25353320
Francesco Abeni for GiBiLogichttp://extensions.gibilogic.com - [email protected]
Feedback please!