Using and reusing CakePHP plugins

Using and Reusing

                               PLUGINS
                      Across                   applications




CakeFest 2010 - Chicago                                       Pierre MARTIN
ME
     June 2008 - CakePHP 1.2 beta

                CakePHP-fr




@pierremartin                http://pierre-martin.fr
YOU



                        ?
Used a plugin

Wrote a plugin

Reuse regularly
WHY
Spaghetti code

                   Libraries
          OOP, Templates
  MVC and Frameworks
       Fat Models    Skinny Controllers


+ Reusable classes (Behaviors, Components, Helpers)
REUSING CODE

             Plugins

                 Or

               CPSR


Copy and Paste / Search and Replace :)
HOW
/APP/PLUGINS
my_plugin/
  my_plugin_app_model.php            locale/
  my_plugin_app_controller.php           eng/
  models/                                    LC_MESSAGES/
     behaviors/                                 my_plugin.po (__d())
     my_plugin_foo.php               webroot/
     my_plugin_bar.php                   css/
  controllers/                               style.css
     components/                             other.css
     my_plugin_foos_controller.php       img/
     my_plugin_bars_controller.php           logo.png
  views/                                 js/
     helpers/                                foobar.js
     layouts/                        tests/
     elements/                       libs/
     my_plugin_foos/                 vendors/
        index.ctp
     my_plugin_bars/
        add.ctp
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’));
PLUGIN.THING
It works for everything!

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

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

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

// Libraries, Vendors, Custom routes...
App::import(‘Lib’, ‘Chuck.Norris’);
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’));
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’);
Source: @dogmatic69
EXTENDING
 PLUGINS
WHY?

  Appearance customization

       App specific logic

Changing features, redirections...

        Adding features
“USERS” PLUGIN

• User model
 • id, username, password
• Users Controller
 • login, logout, register, reset_password
• Views
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 feature
   echo $this->Form->input(‘Profile.newsletter’, array(
       ‘label’ => __(‘Suscribe to our newsletter’, true),
       ‘type’ => ‘checkbox’));
   echo $this->Form->end(__(‘I want to be awesome!’, true));
?>
MODELS
<?php
App::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() { }
}
?>
CONTROLLERS
<?php
App::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() { }
}
?>
TO
                                                                         DO
                                                                            I               mp
                         CONTROLLERS                                                                 rov
                                                                                                        em
                                                                                                             e
Router::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);
}
... AND IT WORKS WITH
          EVERYTHING
    Helpers, Libraries, Components, Behaviors...



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

}
TIPS AND
 TRICKS
 Serious stuff coming!
DON’T
TRUST ME!
 Unless you’ve tried it yourself
REUSE EXISTING PLUGINS
CakePHP’s main feature is its community
CakePackages.com:
 •548 CakePHP related projects
 •284 developers
KISS




Refactor          Extend
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!
COMPONENTS ARE THE KEY!


    Add some magic in your 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;
      }
    }
}
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);
      }
    }
}
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());
          }
      }
    }
}
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);
    }

}
HELPERS THAT HELP

 •   Reduce PHP code in views

 •      Unique entry point

 •      Deal with elements

 •   Performance optimization
... 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');
	

     }
    }

}
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);
	

   }
}
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');
            // [...]
      }
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 logic
    public function isStuffable(Model $Model, $id) {
        return true;
    }

}
HIGHLIGHT ERRORS
               Trigger 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));
  }
}
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
... AND ALSO
• Write   tests

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

• Document        your code

• Provide   interfaces to implement (Lib)

• Write   tests... really!
WHAT IS
MISSING?
NAMESPACES

 ClassRegistry downsides


MyPluginFoobarsController
PLUGINS DIRECTORY

        Reduce plugin duplication

“Diversity is good... with moderation”



           CakePackages.com
PLUGIN MANAGER

         Shell


   Generic installer

      Migrations
METADATA

       Version


    Dependencies


Implemented “Interface”
QUESTIONS?


@pierremartin   http://pierre-martin.fr
1 sur 45

Recommandé

Empowering users: modifying the admin experience par
Empowering users: modifying the admin experienceEmpowering users: modifying the admin experience
Empowering users: modifying the admin experienceBeth Soderberg
1.5K vues33 diapositives
Magento Live Australia 2016: Request Flow par
Magento Live Australia 2016: Request FlowMagento Live Australia 2016: Request Flow
Magento Live Australia 2016: Request FlowVrann Tulika
303 vues45 diapositives
TYPO3 Flow 2.0 (T3CON13 San Francisco) par
TYPO3 Flow 2.0 (T3CON13 San Francisco)TYPO3 Flow 2.0 (T3CON13 San Francisco)
TYPO3 Flow 2.0 (T3CON13 San Francisco)Robert Lemke
2.5K vues53 diapositives
Goodbye hook_menu() - Routing and Menus in Drupal 8 par
Goodbye hook_menu() - Routing and Menus in Drupal 8Goodbye hook_menu() - Routing and Menus in Drupal 8
Goodbye hook_menu() - Routing and Menus in Drupal 8Exove
5.4K vues33 diapositives
Bacbkone js par
Bacbkone jsBacbkone js
Bacbkone jsАртём Курапов
483 vues32 diapositives
Drupal 8 Routing par
Drupal 8 RoutingDrupal 8 Routing
Drupal 8 RoutingYuriy Gerasimov
6.8K vues34 diapositives

Contenu connexe

Tendances

The state of Symfony2 - SymfonyDay 2010 par
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010Fabien Potencier
1.9K vues112 diapositives
Django Class-based views (Slovenian) par
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Luka Zakrajšek
1.1K vues26 diapositives
Comment pages 002 par
Comment pages 002Comment pages 002
Comment pages 002RiNi Ft
95 vues2 diapositives
Matters of State par
Matters of StateMatters of State
Matters of StateKris Wallsmith
16.4K vues87 diapositives
Symfony CoP: Form component par
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
815 vues49 diapositives
How kris-writes-symfony-apps-london par
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonKris Wallsmith
3.7K vues75 diapositives

Tendances(20)

The state of Symfony2 - SymfonyDay 2010 par Fabien Potencier
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier1.9K vues
Django Class-based views (Slovenian) par Luka Zakrajšek
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
Luka Zakrajšek1.1K vues
Comment pages 002 par RiNi Ft
Comment pages 002Comment pages 002
Comment pages 002
RiNi Ft95 vues
Symfony CoP: Form component par Samuel ROZE
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
Samuel ROZE815 vues
How kris-writes-symfony-apps-london par Kris Wallsmith
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
Kris Wallsmith3.7K vues
Mulberry: A Mobile App Development Toolkit par Rebecca Murphey
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
Rebecca Murphey1.9K vues
How I started to love design patterns par Samuel ROZE
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
Samuel ROZE1.3K vues
Binary Studio Academy 2016: Laravel Controllers par Binary Studio
Binary Studio Academy 2016: Laravel ControllersBinary Studio Academy 2016: Laravel Controllers
Binary Studio Academy 2016: Laravel Controllers
Binary Studio272 vues
How Kris Writes Symfony Apps par Kris Wallsmith
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Kris Wallsmith17.1K vues
Building Large jQuery Applications par Rebecca Murphey
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
Rebecca Murphey7.8K vues
Drupal is Stupid (But I Love It Anyway) par brockboland
Drupal is Stupid (But I Love It Anyway)Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)
brockboland3.3K vues
Unit and Functional Testing with Symfony2 par Fabien Potencier
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
Fabien Potencier20.4K vues
Settings API - Oslo WordPress Meetup - November 22, 2011 par WPOslo
Settings API - Oslo WordPress Meetup - November 22, 2011Settings API - Oslo WordPress Meetup - November 22, 2011
Settings API - Oslo WordPress Meetup - November 22, 2011
WPOslo281 vues
How I started to love design patterns par Samuel ROZE
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
Samuel ROZE1.4K vues

Similaire à Using and reusing CakePHP plugins

Unit testing after Zend Framework 1.8 par
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
33.7K vues79 diapositives
Doctrine For Beginners par
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
1.5K vues69 diapositives
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013) par
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)arcware
4.2K vues52 diapositives
Quality Use Of Plugin par
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of PluginYasuo Harada
1.2K vues31 diapositives
Catalyst patterns-yapc-eu-2016 par
Catalyst patterns-yapc-eu-2016Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016John Napiorkowski
305 vues61 diapositives
Getting the Most Out of jQuery Widgets par
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
5K vues51 diapositives

Similaire à Using and reusing CakePHP plugins(20)

10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013) par arcware
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
arcware4.2K vues
Getting the Most Out of jQuery Widgets par velveeta_512
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
velveeta_5125K vues
Building scalable products with WordPress - WordCamp London 2018 par Elliot Taylor
Building scalable products with WordPress - WordCamp London 2018Building scalable products with WordPress - WordCamp London 2018
Building scalable products with WordPress - WordCamp London 2018
Elliot Taylor36 vues
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I... par ZFConf Conference
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference1.7K vues
Easy rest service using PHP reflection api par Matthieu Aubry
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection api
Matthieu Aubry42.5K vues
Single Page Applications in Angular (italiano) par Fabio Biondi
Single Page Applications in Angular (italiano)Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)
Fabio Biondi558 vues
Using Geeklog as a Web Application Framework par Dirk Haun
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application Framework
Dirk Haun894 vues
Building Lithium Apps par Nate Abele
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
Nate Abele5.7K vues
Introducing Rendr: Run your Backbone.js apps on the client and server par Spike Brehm
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
Spike Brehm39.7K vues

Plus de Pierre MARTIN

Introduction à CakePHP par
Introduction à CakePHPIntroduction à CakePHP
Introduction à CakePHPPierre MARTIN
2K vues49 diapositives
Building custom APIs par
Building custom APIsBuilding custom APIs
Building custom APIsPierre MARTIN
707 vues28 diapositives
Test and API-driven development of CakePHP Behaviors par
Test and API-driven development of CakePHP BehaviorsTest and API-driven development of CakePHP Behaviors
Test and API-driven development of CakePHP BehaviorsPierre MARTIN
1.2K vues13 diapositives
Recipes for successful CakePHP projects par
Recipes for successful CakePHP projectsRecipes for successful CakePHP projects
Recipes for successful CakePHP projectsPierre MARTIN
1.5K vues36 diapositives
The CakePHP Media Plugin par
The CakePHP Media PluginThe CakePHP Media Plugin
The CakePHP Media PluginPierre MARTIN
4.3K vues42 diapositives
Internationalizing CakePHP Applications par
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsPierre MARTIN
5.8K vues18 diapositives

Plus de Pierre MARTIN(6)

Test and API-driven development of CakePHP Behaviors par Pierre MARTIN
Test and API-driven development of CakePHP BehaviorsTest and API-driven development of CakePHP Behaviors
Test and API-driven development of CakePHP Behaviors
Pierre MARTIN1.2K vues
Recipes for successful CakePHP projects par Pierre MARTIN
Recipes for successful CakePHP projectsRecipes for successful CakePHP projects
Recipes for successful CakePHP projects
Pierre MARTIN1.5K vues
Internationalizing CakePHP Applications par Pierre MARTIN
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP Applications
Pierre MARTIN5.8K vues

Dernier

How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... par
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...ShapeBlue
166 vues28 diapositives
Kyo - Functional Scala 2023.pdf par
Kyo - Functional Scala 2023.pdfKyo - Functional Scala 2023.pdf
Kyo - Functional Scala 2023.pdfFlavio W. Brasil
457 vues92 diapositives
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti... par
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...ShapeBlue
139 vues29 diapositives
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlue par
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlueMigrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlue
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlueShapeBlue
218 vues20 diapositives
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda... par
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...ShapeBlue
161 vues13 diapositives
Business Analyst Series 2023 - Week 4 Session 7 par
Business Analyst Series 2023 -  Week 4 Session 7Business Analyst Series 2023 -  Week 4 Session 7
Business Analyst Series 2023 - Week 4 Session 7DianaGray10
139 vues31 diapositives

Dernier(20)

How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... par ShapeBlue
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
ShapeBlue166 vues
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti... par ShapeBlue
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...
DRaaS using Snapshot copy and destination selection (DRaaS) - Alexandre Matti...
ShapeBlue139 vues
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlue par ShapeBlue
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlueMigrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlue
Migrating VMware Infra to KVM Using CloudStack - Nicolas Vazquez - ShapeBlue
ShapeBlue218 vues
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda... par ShapeBlue
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...
ShapeBlue161 vues
Business Analyst Series 2023 - Week 4 Session 7 par DianaGray10
Business Analyst Series 2023 -  Week 4 Session 7Business Analyst Series 2023 -  Week 4 Session 7
Business Analyst Series 2023 - Week 4 Session 7
DianaGray10139 vues
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ... par ShapeBlue
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
ShapeBlue126 vues
"Surviving highload with Node.js", Andrii Shumada par Fwdays
"Surviving highload with Node.js", Andrii Shumada "Surviving highload with Node.js", Andrii Shumada
"Surviving highload with Node.js", Andrii Shumada
Fwdays56 vues
State of the Union - Rohit Yadav - Apache CloudStack par ShapeBlue
State of the Union - Rohit Yadav - Apache CloudStackState of the Union - Rohit Yadav - Apache CloudStack
State of the Union - Rohit Yadav - Apache CloudStack
ShapeBlue297 vues
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava... par ShapeBlue
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...
ShapeBlue145 vues
The Role of Patterns in the Era of Large Language Models par Yunyao Li
The Role of Patterns in the Era of Large Language ModelsThe Role of Patterns in the Era of Large Language Models
The Role of Patterns in the Era of Large Language Models
Yunyao Li85 vues
DRBD Deep Dive - Philipp Reisner - LINBIT par ShapeBlue
DRBD Deep Dive - Philipp Reisner - LINBITDRBD Deep Dive - Philipp Reisner - LINBIT
DRBD Deep Dive - Philipp Reisner - LINBIT
ShapeBlue180 vues
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue par ShapeBlue
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlueWhat’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue
ShapeBlue263 vues
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas... par Bernd Ruecker
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
Bernd Ruecker54 vues
Keynote Talk: Open Source is Not Dead - Charles Schulz - Vates par ShapeBlue
Keynote Talk: Open Source is Not Dead - Charles Schulz - VatesKeynote Talk: Open Source is Not Dead - Charles Schulz - Vates
Keynote Talk: Open Source is Not Dead - Charles Schulz - Vates
ShapeBlue252 vues
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue par ShapeBlue
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlueCloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue
ShapeBlue135 vues
CloudStack and GitOps at Enterprise Scale - Alex Dometrius, Rene Glover - AT&T par ShapeBlue
CloudStack and GitOps at Enterprise Scale - Alex Dometrius, Rene Glover - AT&TCloudStack and GitOps at Enterprise Scale - Alex Dometrius, Rene Glover - AT&T
CloudStack and GitOps at Enterprise Scale - Alex Dometrius, Rene Glover - AT&T
ShapeBlue152 vues
Transitioning from VMware vCloud to Apache CloudStack: A Path to Profitabilit... par ShapeBlue
Transitioning from VMware vCloud to Apache CloudStack: A Path to Profitabilit...Transitioning from VMware vCloud to Apache CloudStack: A Path to Profitabilit...
Transitioning from VMware vCloud to Apache CloudStack: A Path to Profitabilit...
ShapeBlue159 vues
Zero to Cloud Hero: Crafting a Private Cloud from Scratch with XCP-ng, Xen Or... par ShapeBlue
Zero to Cloud Hero: Crafting a Private Cloud from Scratch with XCP-ng, Xen Or...Zero to Cloud Hero: Crafting a Private Cloud from Scratch with XCP-ng, Xen Or...
Zero to Cloud Hero: Crafting a Private Cloud from Scratch with XCP-ng, Xen Or...
ShapeBlue198 vues

Using and reusing CakePHP plugins

  • 1. Using and Reusing PLUGINS Across applications CakeFest 2010 - Chicago Pierre MARTIN
  • 2. ME June 2008 - CakePHP 1.2 beta CakePHP-fr @pierremartin http://pierre-martin.fr
  • 3. YOU ? Used a plugin Wrote a plugin Reuse regularly
  • 4. WHY
  • 5. Spaghetti code Libraries OOP, Templates MVC and Frameworks Fat Models Skinny Controllers + Reusable classes (Behaviors, Components, Helpers)
  • 6. REUSING CODE Plugins Or CPSR Copy and Paste / Search and Replace :)
  • 7. HOW
  • 8. /APP/PLUGINS my_plugin/ my_plugin_app_model.php locale/ my_plugin_app_controller.php eng/ models/ LC_MESSAGES/ behaviors/ my_plugin.po (__d()) my_plugin_foo.php webroot/ my_plugin_bar.php css/ controllers/ style.css components/ other.css my_plugin_foos_controller.php img/ my_plugin_bars_controller.php logo.png views/ js/ helpers/ foobar.js layouts/ tests/ elements/ libs/ my_plugin_foos/ vendors/ index.ctp my_plugin_bars/ add.ctp
  • 9. 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’));
  • 10. PLUGIN.THING It works for everything! // Behaviors public $actsAs = array(‘Comments.Commentable’); // Components public $components = array(‘Twitter.TwitterAuth’); // Helpers public $helpers = array(‘Tags.TagCloud’); // Libraries, Vendors, Custom routes... App::import(‘Lib’, ‘Chuck.Norris’);
  • 11. 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’));
  • 15. WHY? Appearance customization App specific logic Changing features, redirections... Adding features
  • 16. “USERS” PLUGIN • User model • id, username, password • Users Controller • login, logout, register, reset_password • Views
  • 17. 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 feature echo $this->Form->input(‘Profile.newsletter’, array( ‘label’ => __(‘Suscribe to our newsletter’, true), ‘type’ => ‘checkbox’)); echo $this->Form->end(__(‘I want to be awesome!’, true)); ?>
  • 18. MODELS <?php App::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() { } } ?>
  • 19. CONTROLLERS <?php App::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() { } } ?>
  • 20. TO DO I mp CONTROLLERS rov em e Router::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); }
  • 21. ... AND IT WORKS WITH EVERYTHING Helpers, Libraries, Components, Behaviors... App::import(‘Behavior’, ‘Comments.Commentable’); class MyCommentable extends Commentable { }
  • 22. TIPS AND TRICKS Serious stuff coming!
  • 23. DON’T TRUST ME! Unless you’ve tried it yourself
  • 24. REUSE EXISTING PLUGINS CakePHP’s main feature is its community CakePackages.com: •548 CakePHP related projects •284 developers
  • 25. KISS Refactor Extend
  • 26. 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!
  • 27. COMPONENTS ARE THE KEY! Add some magic in your plugins
  • 28. HELPER AUTOLOADING class CommentManager extends Object { public $autoHelper = true; public $helperName = ‘Comments.CommentWidget’; public function beforeRender(Controller $Controller) { if ($this->autoHelper) { $Controller->helpers[] = $helperName; } } }
  • 29. 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); } } }
  • 30. 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()); } } } }
  • 31. 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); } }
  • 32. HELPERS THAT HELP • Reduce PHP code in views • Unique entry point • Deal with elements • Performance optimization
  • 33. ... 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'); } } }
  • 34. 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); } }
  • 35. 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'); // [...] }
  • 36. 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 logic public function isStuffable(Model $Model, $id) { return true; } }
  • 37. HIGHLIGHT ERRORS Trigger 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)); } }
  • 38. 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
  • 39. ... AND ALSO • Write tests • Use __d(‘myplugin’, ‘This is my text’); • Document your code • Provide interfaces to implement (Lib) • Write tests... really!
  • 42. PLUGINS DIRECTORY Reduce plugin duplication “Diversity is good... with moderation” CakePackages.com
  • 43. PLUGIN MANAGER Shell Generic installer Migrations
  • 44. METADATA Version Dependencies Implemented “Interface”
  • 45. QUESTIONS? @pierremartin http://pierre-martin.fr

Notes de l'éditeur

  1. CakePHP-fr now: 15000 messages, 800 members, 500 visitors a day
  2. Adding associations, behaviors Changing redirection, workflow...
  3. Users is something that is common to most of the applications There are common features, but User data / associations / workflow is different across applications
  4. Here is a quick and dirty render overriding allowing you to load parents views if needed
  5. I will introduce some practices I find useful and I learnt when trying to reuse plugins I wrote This is just something personal... so take it as it!
  6. Adding associations, behaviors Changing redirection, workflow...
  7. The keywork here is &amp;#x201C;magic&amp;#x201D;. You can make your plugin easier to integrate in an application by adding some automagical features in a Component attached to Controller that needs it. However, be sure to provide a way to disable this magic if needed.
  8. CakePHP-fr now: 15000 messages, 800 members, 500 visitors a day