45
PLUGINS Across applications Using and Reusing CakeFest 2010 - Chicago Pierre MARTIN

Using and reusing CakePHP plugins

Embed Size (px)

DESCRIPTION

Using and reusing plugins across CakePHP applications - CakeFest 2010 (Chicago)

Citation preview

Page 1: Using and reusing CakePHP plugins

PLUGINSAcross applications

Using and Reusing

CakeFest 2010 - Chicago Pierre MARTIN

Page 2: Using and reusing CakePHP plugins

ME

@pierremartin http://pierre-martin.fr

June 2008 - CakePHP 1.2 beta

CakePHP-fr

Page 3: Using and reusing CakePHP plugins

YOU

?Used a plugin

Wrote a plugin

Reuse regularly

Page 4: Using and reusing CakePHP plugins

WHY

Page 5: Using and reusing CakePHP plugins

Spaghetti code

Libraries

OOP, TemplatesMVC and Frameworks

+ Reusable classes (Behaviors, Components, Helpers)

Fat ModelsSkinny Controllers

Page 6: Using and reusing CakePHP plugins

REUSING CODE

Plugins

Or

C P S R

Copy and Paste / Search and Replace :)

Page 7: Using and reusing CakePHP plugins

HOW

Page 8: Using and reusing CakePHP plugins

/APP/PLUGINSmy_plugin/ my_plugin_app_model.php my_plugin_app_controller.php models/ behaviors/ my_plugin_foo.php my_plugin_bar.php controllers/ components/ my_plugin_foos_controller.php my_plugin_bars_controller.php views/ helpers/ layouts/ elements/ my_plugin_foos/ index.ctp my_plugin_bars/ add.ctp

locale/ eng/ LC_MESSAGES/ my_plugin.po (__d())webroot/ css/

style.cssother.css

img/logo.png

js/foobar.js

tests/libs/vendors/

Page 9: Using and reusing CakePHP plugins

MODELS/app/plugins/users/models/user.php

ClassRegistry::init(‘Users.User’);

App::import(‘Model’, ‘Users.User’);

public $belongsTo = array(‘Users.User’);public $belongsTo = array(

‘User’ => array(‘className’ => ‘Users.User’));

public $belongsTo = array(‘User’ => array(

‘className’ => ‘Users.UsersUser’));

Page 10: Using and reusing CakePHP plugins

PLUGIN.THING

It works for everything!

// Behaviorspublic $actsAs = array(‘Comments.Commentable’);

// Componentspublic $components = array(‘Twitter.TwitterAuth’);

// Helperspublic $helpers = array(‘Tags.TagCloud’);

// Libraries, Vendors, Custom routes...App::import(‘Lib’, ‘Chuck.Norris’);

Page 11: Using and reusing CakePHP plugins

ACTIONS / ELEMENTS/app/plugins/users/controllers/users_controller.php

/app/plugins/users/views/elements/login.ctp

$this->redirect(‘plugin’ => ‘users’, ‘controller’ => ‘users’, ‘action’ => ‘register’);$this->redirect(‘plugin’ => null, ‘controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’);

$this->Html->link(‘plugin’ => ‘users’, ‘controller’ => ‘users’, ‘action’ => ‘index’);// Will generate http://domain.com/users

$this->element(‘login’, array(‘plugin’ => ‘users’, ‘foo’ => ‘bar’));

Page 12: Using and reusing CakePHP plugins

ASSETS/app/plugins/jquery/webroot/js/jquery.js

/app/plugins/jquery/webroot/css/jquery.ui.css

/app/plugins/jquery/webroot/img/subdir/logo.png

$this->Html->script(‘/jquery/js/jquery.js’);

$this->Html->css(‘/jquery/css/jquery.ui.css’);

$this->Html->image(‘/jquery/img/subdir/logo.png’);

Page 13: Using and reusing CakePHP plugins

Source: @dogmatic69

Page 14: Using and reusing CakePHP plugins

EXTENDING PLUGINS

Page 15: Using and reusing CakePHP plugins

WHY?

Appearance customization

App specific logic

Changing features, redirections...

Adding features

Page 16: Using and reusing CakePHP plugins

“USERS” PLUGIN

• User model

• id, username, password

• Users Controller

• login, logout, register, reset_password

• Views

Page 17: Using and reusing CakePHP plugins

VIEWS/app/plugins/users/views/users/register.ctp/app/views/plugins/users/users/register.ctp

<h1><?php __(‘Create a new account on Awesomeness.org’); ?><?php

echo $this->Form->create(‘User’);echo $this->Form->input(‘username’);echo $this->Form->input(‘password’);echo $this->Form->input(‘password_confirm’);// App specific featureecho $this->Form->input(‘Profile.newsletter’, array(

‘label’ => __(‘Suscribe to our newsletter’, true),‘type’ => ‘checkbox’));

echo $this->Form->end(__(‘I want to be awesome!’, true));?>

Page 18: Using and reusing CakePHP plugins

MODELS<?phpApp::import(‘Model’, ‘Users.User’);class MyUser extends User {

// [...]public $hasOne = array(‘Profile’);// [...]public function __construct($id = false, $table = null, $ds = null) {

parent::__construct($id, $table, $ds);$this->validate[‘username’][‘length’] = array(

‘rule’ => array(‘minLength’, 5));}// [...]public function register($data) {

$success = parent::register($data);if ($success) {

// Your business logic here}return $success;

}// [...]public function foobar() { }

}?>

Page 19: Using and reusing CakePHP plugins

CONTROLLERS<?phpApp::import(‘Controller’, ‘Users.Users’);class MyUsersController extends UsersController {

// [...]public function beforeFilter() {

$this->User = ClassRegistry::init('MyUser');parent::beforeFilter();$this->Auth->deny('index');

}// [...]public function register() {

if (!empty($this->data)) {if ($this->User->register($this->data)) {

// Your app specific logic here$this->redirect(‘controller’ => ‘pages’, ‘action’ => ‘display’, ‘welcome’);

}}parent::register();

}// [...]public function foobar() { }

}?>

Page 20: Using and reusing CakePHP plugins

CONTROLLERSRouter::connect(

'/users/:action/*',array('plugin' => ‘users’, 'controller' => 'users'));

Router ::connect('/users/:action/*',array('plugin' => null, 'controller' => 'my_users'));

public function render($action = null, $layout = null, $file = null) {if (is_null($action)) {

$action = $this->action;}if ($action !== false) {

if (!file_exists(VIEWS . 'my_users' . DS . $action . '.ctp')) { $file = App::pluginPath('users') . 'views' . DS . 'users' . DS . $action . '.ctp'; }

}return parent::render($action, $layout, $file);

}

TODO Improve me

Page 21: Using and reusing CakePHP plugins

... AND IT WORKS WITH EVERYTHING

App::import(‘Behavior’, ‘Comments.Commentable’);class MyCommentable extends Commentable {

}

Helpers, Libraries, Components, Behaviors...

Page 22: Using and reusing CakePHP plugins

TIPS AND TRICKS

Serious stuff coming!

Page 23: Using and reusing CakePHP plugins

DON’T TRUST ME!

Unless you’ve tried it yourself

Page 24: Using and reusing CakePHP plugins

REUSE EXISTING PLUGINS

CakePackages.com:•548 CakePHP related projects•284 developers

CakePHP’s main feature is its community

Page 25: Using and reusing CakePHP plugins

KISS

ExtendRefactor

Page 26: Using and reusing CakePHP plugins

USE OBJECTS ATTRIBUTES// Models$this->alias$this->name$this->displayField$this->primaryKey

$this->data[‘User’][‘id’]; // Before$this->data[$this->alias][$this->primaryKey]; // After

// Controllers$this->plugin$this->modelClass // MyModel$this->modelKey // my_model$this->name

Add attributes to your classes!

Page 27: Using and reusing CakePHP plugins

COMPONENTS ARE THE KEY!

Add some magic in your plugins

Page 28: Using and reusing CakePHP plugins

HELPER AUTOLOADING

class CommentManager extends Object {public $autoHelper = true;

public $helperName = ‘Comments.CommentWidget’;

public function beforeRender(Controller $Controller) {if ($this->autoHelper) {

$Controller->helpers[] = $helperName;}

}}

Page 29: Using and reusing CakePHP plugins

BEHAVIOR AUTOLOADING

class CommentManager extends Object {public $autoBehavior = true;

public $behaviorName = ‘Comments.Commentable’;

public function startup(Controller $Controller) {$Model = $Controller->{$Controller->modelClass};if ($autoBehavior && !$Model->Behaviors->attached($this->behaviorName)) { $Model->Behaviors->attach($this->behaviorName);}

}}

Page 30: Using and reusing CakePHP plugins

AUTODETECTED ACTIONS

class CommentManager extends Object {public $autoActions = true;

public function startup(Controller $Controller) {if ($autoActions) {

if (!empty($Controller->data[‘Comment’])) {// [...] Automatically save the comment$Controller->redirect($Controller->referer());

}}

}}

Page 31: Using and reusing CakePHP plugins

AUTO DATA FETCHING

class FoobarManager extends Object {

public function beforeRender(Controller $Controller) {$data = [...]; // Your logic here to get the correct data for the view$Controller->set(‘data_for_foobar_helper’, $data);

}

}

Page 32: Using and reusing CakePHP plugins

HELPERS THAT HELP

• Reduce PHP code in views

• Unique entry point

• Deal with elements

• Performance optimization

Page 33: Using and reusing CakePHP plugins

... BEHIND THE SCENE

class FoobarHelper extends AppHelper {

public function beforeRender() { if (ClassRegistry::isKeySet('view')) { $View = ClassRegistry::getObject('view'); $this->_data = $View->getVar('data_for_foobar_helper');

}}

}

Page 34: Using and reusing CakePHP plugins

public function display($element = 'carts/view', $options) { if (!ClassRegistry::isKeySet('view')) { return; }

if (empty($cartData)) { if (is_a($this->Session, 'SessionHelper') && $this->Session->check('Cart')) { $cartData = $this->Session->read('Cart'); } else { $cartData = $this->requestAction($this->cartRequestUrl); } }

if (empty($cartData)) { trigger_error(__d('cart', 'No cart found.', true), E_USER_NOTICE); } else { // [...] Format the data and add default options (caching...) $options['cartData'] = $cartData; return ClassRegistry::getObject('view')->element($element, $options); }}

Page 35: Using and reusing CakePHP plugins

USE THE CONFIGURE CLASS•With default values

• Configure::load()

public function __construct($id = false, $table = null, $ds = null) { $userClass = Configure::read('App.UserClass'); if (empty($userClass)) { $userClass = 'User'; } $this->belongsTo['User'] = array( 'className' => $userClass, 'foreignKey' => 'user_id');

// [...]}

Page 36: Using and reusing CakePHP plugins

CALLBACKS / HOOKS

class StuffableBehavior extends ModelBehavior {

public function doStuff(Model $Model, $id) {if ($Model->isStuffable($id)) {

// [...]if (method_exists($Model, ‘afterStuff’)) {

$Model->afterStuff();}

}}

// Fallback, default logicpublic function isStuffable(Model $Model, $id) {

return true;}

}

Page 37: Using and reusing CakePHP plugins

HIGHLIGHT ERRORSTrigger errors for the developer

Throw Exceptions for the User

$mandatory = Configure::read('Foo.bar');if (empty($mandatory)) {

trigger_error(‘You must configure your Foobar’, E_USER_ERROR);}

public function doStuff($id) {$Model->id = $id;if (!$Model->exists($id)) {

throw new OutOfBoundsException(__(‘Invalid object’, true));}

}

Page 38: Using and reusing CakePHP plugins

MAKE MIGRATIONS EASY• Version your code, tag versions (KISS, Extend, Refactor)

•Document API changes between versions

• Use CakeDC’s awesome Migrations plugin!

• Schema updates

• Initial data

• Configuration assistance

Page 39: Using and reusing CakePHP plugins

... AND ALSO•Write tests

• Use __d(‘myplugin’, ‘This is my text’);

•Document your code

• Provide interfaces to implement (Lib)

•Write tests... really!

Page 40: Using and reusing CakePHP plugins

WHAT IS MISSING?

Page 41: Using and reusing CakePHP plugins

NAMESPACES

MyPluginFoobarsController

ClassRegistry downsides

Page 42: Using and reusing CakePHP plugins

PLUGINS DIRECTORY

CakePackages.com

Reduce plugin duplication

“Diversity is good... with moderation”

Page 43: Using and reusing CakePHP plugins

PLUGIN MANAGER

Generic installer

Shell

Migrations

Page 44: Using and reusing CakePHP plugins

METADATA

Implemented “Interface”

Dependencies

Version

Page 45: Using and reusing CakePHP plugins

QUESTIONS?

@pierremartin http://pierre-martin.fr