SlideShare uma empresa Scribd logo
1 de 45
Baixar para ler offline
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

Mais conteúdo relacionado

Mais procurados

The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
Luka Zakrajšek
 
Comment pages 002
Comment pages 002Comment pages 002
Comment pages 002
RiNi Ft
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
Rebecca Murphey
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
Fabien Potencier
 
Settings API - Oslo WordPress Meetup - November 22, 2011
Settings API - Oslo WordPress Meetup - November 22, 2011Settings API - Oslo WordPress Meetup - November 22, 2011
Settings API - Oslo WordPress Meetup - November 22, 2011
WPOslo
 
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 AjaxRuby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 Ajax
Wen-Tien Chang
 

Mais procurados (20)

The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
 
Comment pages 002
Comment pages 002Comment pages 002
Comment pages 002
 
Matters of State
Matters of StateMatters of State
Matters of State
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
The Settings API
The Settings APIThe Settings API
The Settings API
 
Binary Studio Academy 2016: Laravel Controllers
Binary Studio Academy 2016: Laravel ControllersBinary Studio Academy 2016: Laravel Controllers
Binary Studio Academy 2016: Laravel Controllers
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
 
Settings API - Oslo WordPress Meetup - November 22, 2011
Settings API - Oslo WordPress Meetup - November 22, 2011Settings API - Oslo WordPress Meetup - November 22, 2011
Settings API - Oslo WordPress Meetup - November 22, 2011
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Keeping It Simple
Keeping It SimpleKeeping It Simple
Keeping It Simple
 
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 AjaxRuby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 Ajax
 
Feeds drupal cafe
Feeds drupal cafeFeeds drupal cafe
Feeds drupal cafe
 

Semelhante a Using and reusing CakePHP plugins

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)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
arcware
 
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 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference
 
Introducing Rendr: Run your Backbone.js apps on the client and server
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 Brehm
 

Semelhante a Using and reusing CakePHP plugins (20)

Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
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)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
 
Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
Pluggin creation
Pluggin creationPluggin creation
Pluggin creation
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
Django Vs Rails
Django Vs RailsDjango Vs Rails
Django Vs Rails
 
Building scalable products with WordPress - WordCamp London 2018
Building scalable products with WordPress - WordCamp London 2018Building scalable products with WordPress - WordCamp London 2018
Building scalable products with WordPress - WordCamp London 2018
 
AngularJs-training
AngularJs-trainingAngularJs-training
AngularJs-training
 
CFUGbe talk about Angular JS
CFUGbe talk about Angular JSCFUGbe talk about Angular JS
CFUGbe talk about Angular JS
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
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 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Easy rest service using PHP reflection api
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection api
 
Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)
 
Using Geeklog as a Web Application Framework
Using Geeklog as a Web Application FrameworkUsing Geeklog as a Web Application Framework
Using Geeklog as a Web Application Framework
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
Introducing Rendr: Run your Backbone.js apps on the client and server
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
 

Mais de Pierre MARTIN

Mais de Pierre MARTIN (6)

Introduction à CakePHP
Introduction à CakePHPIntroduction à CakePHP
Introduction à CakePHP
 
Building custom APIs
Building custom APIsBuilding custom APIs
Building custom APIs
 
Test and API-driven development of CakePHP Behaviors
Test and API-driven development of CakePHP BehaviorsTest and API-driven development of CakePHP Behaviors
Test and API-driven development of CakePHP Behaviors
 
Recipes for successful CakePHP projects
Recipes for successful CakePHP projectsRecipes for successful CakePHP projects
Recipes for successful CakePHP projects
 
The CakePHP Media Plugin
The CakePHP Media PluginThe CakePHP Media Plugin
The CakePHP Media Plugin
 
Internationalizing CakePHP Applications
Internationalizing CakePHP ApplicationsInternationalizing CakePHP Applications
Internationalizing CakePHP Applications
 

Último

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Último (20)

Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 

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

Notas do Editor

  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