the future of plugin dev

130
THE FUTURE OF PLUGIN DEV Brandon Kelly

Upload: brandonkelly212

Post on 28-Jul-2015

83 views

Category:

Presentations & Public Speaking


3 download

TRANSCRIPT

Page 1: The Future of Plugin Dev

THE FUTURE OF PLUGIN DEV

Brandon Kelly

Page 2: The Future of Plugin Dev

Craft 3 Dev Preview coming in May

Page 3: The Future of Plugin Dev

Powered by Yii 2

Page 4: The Future of Plugin Dev

Totally refactored

Page 5: The Future of Plugin Dev

Faster

Page 6: The Future of Plugin Dev

Requests/second(bigger is better)

EE Joomla Drupal 7 Craft 2 Craft 3

17.52

9.799.05

4.784.17

Page 7: The Future of Plugin Dev

Response Times(in milliseconds - smaller is better)

EE Joomla Drupal 7 Craft 2 Craft 3

57

102110

209240

Page 8: The Future of Plugin Dev

buildwithcraft.com/3

Page 9: The Future of Plugin Dev

What has changed for Craft has also changed

for plugins

Page 10: The Future of Plugin Dev

• What’s changed in PHP • What’s changed in Yii • What’s changed in Craft • Port a plugin

Game plan

Page 11: The Future of Plugin Dev

PHP

Page 12: The Future of Plugin Dev

Yii 2 and Craft 3 bothrequire PHP 5.4(Yii 1 required 5.1; Craft 2 required 5.3)

Page 13: The Future of Plugin Dev

• JSON extension • ZIP extension • Upload progress-tracking hooks • DateTime/DateTimeZone

PHP 5.2 features

Page 14: The Future of Plugin Dev

• Namespaces • Late static bindings • Closures

PHP 5.3 features

Page 15: The Future of Plugin Dev

Namespaces

namespace craft\plugins\foo;

class Plugin { public function getClassName() { return get_class($this); } }

$plugin = new Plugin(); echo $plugin->getClassName(); // => "craft\plugins\foo\Plugin"

Page 16: The Future of Plugin Dev

Late static bindings

class Object { public static function className() { return get_called_class(); } }

class Foo extends Object { }

echo Foo::className(); // => "Foo"

Page 17: The Future of Plugin Dev

Closures

$obj->on('thing', function($event) { // ... });

Page 18: The Future of Plugin Dev

• Traits • Short array syntax • Function array dereferencing • Class member access on

instantiation

PHP 5.4 Features

Page 19: The Future of Plugin Dev

Traits

trait FooTrait { public $bar = 'bar'; }

class Foo { use FooTrait; }

$foo = new Foo(); echo $foo->bar; // => "bar"

Page 20: The Future of Plugin Dev

Short array syntax

$myIndexedArray = [ 'key1' => 1, 'key2' => 2 ];

$myBoringArray = [1, 2, 3];

Page 21: The Future of Plugin Dev

Function array dereferencing

function foo() { return ['one', 'two', 'three']; }

echo foo()[0]; // => "one"

Page 22: The Future of Plugin Dev

Class member access on instantiation

class Foo { public function bar() { // ... } }

(new Foo()) ->bar();

Page 23: The Future of Plugin Dev

Yii

Page 24: The Future of Plugin Dev

Class Names and Namespaces

Page 25: The Future of Plugin Dev

• No more “C” class name prefix • Namespaces resemble the folder

structure (PSR-4) • No more Yii::import() • Path aliases define the root

namespaces for the autoloader

Class name/namespace changes

Page 26: The Future of Plugin Dev

Path alias example

Yii::setAlias('@foo', '/path/to/foo');

echo Yii::getAlias('@foo/subpath'); // => "/path/to/foo/subpath"

Page 27: The Future of Plugin Dev

Important path aliases to be aware ofPath Alias Server Path Root Namespace

@yii …/app/vendor/yiisoft/yii2 yii

@craft/app …/app craft\app

@craft/plugins/foo …/plugins/foo craft\plugins\foo

Page 28: The Future of Plugin Dev

Objects and Components

Page 29: The Future of Plugin Dev

• Base class for everything • Property getters/setters • Event handling • Behaviors

CComponent in Yii 1

Page 30: The Future of Plugin Dev

• yii\base\Object • yii\base\Component

Split into two classes in Yii 2

Page 31: The Future of Plugin Dev

• The new base class for everything

• Only does propertygetters/setters

Object

Page 32: The Future of Plugin Dev

• Extends Object • Event handling • Behaviors

Component

Page 33: The Future of Plugin Dev

• Extend Object for classes that don’t do anything besides represent data (glorified arrays)

• Extend Component for everything else

When to use which?

Page 34: The Future of Plugin Dev

• yii\base\Event • yii\db\TableSchema • yii\mail\BaseMessage • yii\web\CookieCollection • yii\web\UrlRule

Object examples

Page 35: The Future of Plugin Dev

• yii\base\Model • yii\db\Connection • yii\i18n\I18N • yii\mail\BaseMailer • yii\web\Request

Component examples

Page 36: The Future of Plugin Dev

Objects are configurable

Page 37: The Future of Plugin Dev

• Define object config settings as public properties

• Pass configuration arrays into the object constructor

Configuring objects

Page 38: The Future of Plugin Dev

Object configuration examples

class User extends Object { public $age; }

$user = new User(['age' => 21]);

Page 39: The Future of Plugin Dev

Object configuration examples

class User extends Object { public function setAge($age) { // ... } public function getAge() { // ... } }

$user = new User(['age' => 21]);

Page 40: The Future of Plugin Dev

Object configuration examples

$user = Yii::createObject('foo\User');

$user = Yii::createObject([ 'class' => 'foo\User', 'age' => 21 ]);

Page 41: The Future of Plugin Dev

Service Locators

Page 42: The Future of Plugin Dev

• Base: yii\di\ServiceLocator • They have sub-components • Components accessed via magic

getters and get() • Subcomponents are created the

first time they’re requested

Service locators

Page 43: The Future of Plugin Dev

Modules

Page 44: The Future of Plugin Dev

• Base: yii\base\Module • They are service locators, so they

have sub-components • They also have controllers, views,

and sub-modules

Modules

Page 45: The Future of Plugin Dev

Applications

Page 46: The Future of Plugin Dev

• Base: yii\base\Application • They are modules • They handle the request • Only one application per request

Applications

Page 47: The Future of Plugin Dev

• Web requests:yii\web\Application

• Console requests:yii\console\Application

Two application classes

Page 48: The Future of Plugin Dev

Controllers

Page 49: The Future of Plugin Dev

• Web and console applications have controllers (no more “console command” classes)

• Controller action routing works the same either way

• They return the response

Controllers

Page 50: The Future of Plugin Dev

Request vs. Response

Page 51: The Future of Plugin Dev

• Request classes define the incoming web/console request

• Response classes define the outgoing web/console response

• The response is automatically sent at the end of the application processing

Request vs. response

Page 52: The Future of Plugin Dev

• Describes the HTTP request • Method (GET, POST, etc.) • Ajax/Pjax/Flash? • Headers, body, parameters • Acceptable content types/languages • HTTP authentication • URL, host name, URI, port, SSL • Referrer, user agent, client IP

yii\web\Request

Page 53: The Future of Plugin Dev

• Describes the console command • Requested route • Requested parameters

yii\console\Request

Page 54: The Future of Plugin Dev

• Defines the HTTP response • Status code (200, 404, etc.) • Headers • Cookies • Body

yii\web\Response

Page 55: The Future of Plugin Dev

• HTML • XML • JSON • JSONP

Web response formats

Page 56: The Future of Plugin Dev

Formatted response examples

$response = Yii::$app->getResponse(); $response->data = $user; $response->format = 'json'; $response->send();

{ "id": 5, "username": "admin", "age": 21 }

Page 57: The Future of Plugin Dev

Formatted response examples

$response = Yii::$app->getResponse(); $response->data = $user; $response->format = 'xml'; $response->send();

<response> <id>5</id> <username>admin</username> <age>21</age> </response>

Page 58: The Future of Plugin Dev

Events

Page 59: The Future of Plugin Dev

• Defined by “onEventName()” methods on component classes

• Attached by setting a magic “onEventName” property

• Raised by calling raiseEvent() within the event method

Events in Yii 1

Page 60: The Future of Plugin Dev

Yii 1 event example

class User extends CComponent { public function wake() { $this->onWake(new CEvent()); } public function onWake($ev) { $this->raiseEvent('onWake', $ev); } }

$user = new User(); $user->onWake = function($ev){/*...*/};

Page 61: The Future of Plugin Dev

• Not explicitly predefined • Attached by calling on() • Raised by calling trigger()

Events in Yii 2

Page 62: The Future of Plugin Dev

Yii 2 event example

class User extends Component { public function wake() { $this->trigger('afterWake'); } }

$user = new User(); $user->on('afterWake', function($ev){ // ... });

Page 63: The Future of Plugin Dev

Class-level Event Handlers

Page 64: The Future of Plugin Dev

Class-level event handler example

Event::on('foo\User', 'afterWake', function($ev) { // ... } );

Page 65: The Future of Plugin Dev

Asset Bundles

Page 66: The Future of Plugin Dev

• For JS and CSS resources • They can have dependencies • They can publish their files into

the web root when registered • They can compile LESS, Sass,

Stylus, and CoffeeScript

Asset bundles

Page 67: The Future of Plugin Dev

Defining an asset bundle

class AppAsset extends AssetBundle { public $sourcePath = '@app/assets'; public $basePath = '@web/assets'; public $depends = [ 'yii\web\JqueryAsset' ]; public $css = [ 'css/app.scss' ]; }

Page 68: The Future of Plugin Dev

Registering an asset bundle

Yii::$app->view->registerAssetBundle( 'foo\AppAsset' );

Page 69: The Future of Plugin Dev

DB Queries

Page 70: The Future of Plugin Dev

• All SELECT queries are defined by yii\db\Query objects

• All other queries are defined by yii\db\Command objects

• Schema-specific SQL is always built with QueryBuilder

DB Queries

Page 71: The Future of Plugin Dev

Query methodsQuery Definition Query Execution

select() distinct() from() where()

innerJoin() leftJoin() rightJoin() groupBy() having() union() orderBy() limit() offset()

batch() each() all() one()

scalar() column() count() sum()

average() min() max()

exists() createCommand()

Page 72: The Future of Plugin Dev

SELECT query examples

$user = (new Query()) ->from('{{%users}}') ->where(['id' => 5]) ->one();

Page 73: The Future of Plugin Dev

SELECT query examples

$users = (new Query()) ->from('{{%users}}') ->where('age >= :min', [ ':min' => 21 ]) ->all();

Page 74: The Future of Plugin Dev

Command methodsQuery Definition Query Management Query Execution

insert() batchInsert()

update() delete()

createTable() renameTable() dropTable()

truncateTable() addColumn() dropColumn()

renameColumn() alterColumn() addPrimaryKey() dropPrimaryKey() addForeignKey() dropForeignKey() createIndex() dropIndex()

cache() noCache() getSql() setSql()

getRawSql() prepare() cancel()

bindParam() bindPendingParams()

bindValue() bindValues()

query() queryAll() queryOne()

queryScalar() queryColumn() execute()

Page 75: The Future of Plugin Dev

More query examples

Yii::$app->db->createCommand() ->insert('users', [ 'username' => 'brandon' 'age' => 29 ]) ->execute();

Page 76: The Future of Plugin Dev

More query examples

Yii::$app->db->createCommand() ->update('users', ['age' => 30], ['id' => 5] ) ->execute();

Page 77: The Future of Plugin Dev

How they map togetherQuery Execution Method Command Execution Method

all() queryAll()

one() queryOne()

count() sum()

average() min() max()

exists() scalar()

queryScalar()

column() queryColumn()

Page 78: The Future of Plugin Dev

Active Record

Page 79: The Future of Plugin Dev

• Base: yii\db\ActiveRecord • Records’ ::find() methods

return ActiveQuery objects • ActiveQuery handles the query

definition and active record object population

Active Record

Page 80: The Future of Plugin Dev

Active record query example

$users = User::find() ->where('age => :min', [ ':min' => 21 ]) ->all();

Page 81: The Future of Plugin Dev

Internationalization(I18n)

Page 82: The Future of Plugin Dev

• Yii 2 uses PHP’s Intl extension to format dates, numbers, etc.

• Only very basic single-language fallbacks built-in.

• Only 30% of servers have Intl • What could go wrong?

Internationalization

Page 83: The Future of Plugin Dev

Craft

Page 84: The Future of Plugin Dev

• No more Craft namespace • Namespaces reflect the folder

structure now • Root namespace is craft\app • Plugins’ root namespaces follow

the pattern craft\plugins\foo

Namespaces

Page 85: The Future of Plugin Dev

• Craft 2: craft() returns the Craft\WebApp instance

• Craft 3: Craft::$app returns thecraft\app\web\Application instance

Accessing the Application instance

Page 86: The Future of Plugin Dev

• Magic property names are still available:Craft::$app->entries

• We’ve also added getter methods for all services:Craft::$app->getEntries()

Accessing the Application components

Page 87: The Future of Plugin Dev

• userSession is now user / getUser()

• httpSession is now session / getSession()

Application component name changes

Page 88: The Future of Plugin Dev

Routes and Controllers

Page 89: The Future of Plugin Dev

• In Craft 2, routes pointed to templates by default, and could point to a controller action with a special syntax

• That’s reversed in Craft 2: by default routes point to actions

Routes and controllers

Page 90: The Future of Plugin Dev

Craft 2 route example

return array( // Template route: 'foo' => 'path/to/template', // Controller action route: 'bar' => array( 'action' => 'foo/bar' ) );

Page 91: The Future of Plugin Dev

Craft 3 route example

return [ // Template route: 'foo' => [ 'template' => 'template/path', ], // Controller action route: 'bar' => 'foo/bar' ];

Page 92: The Future of Plugin Dev

• Params captured by URL rules are now passed directly to the action’s arguments

• URL Manager’s setRouteVariables() is now setRouteParams()

Routes and controllers

Page 93: The Future of Plugin Dev

Models

Page 94: The Future of Plugin Dev

• No more defineAttributes() • Attributes must be defined as

public properties on the model • Validation rules must be defined

in the rules() method

Model changes in Craft 3

Page 95: The Future of Plugin Dev

Craft 2 model example

class User extends BaseModel { protected function defineAttributes() { return array( 'id' => AttributeType::Number, 'age' => AttributeType::Number ); } }

Page 96: The Future of Plugin Dev

Craft 3 model example

class User extends Model { public $id; public $age;

public function rules() { return [ ['id,age', 'number'] ]; } }

Page 97: The Future of Plugin Dev

Records

Page 98: The Future of Plugin Dev

• No more defineAttributes() • Column names are determined

by analyzing the table • Validation rules must be defined

in the rules() method • Records no longer create tables

Record changes in Craft 3

Page 99: The Future of Plugin Dev

Templates

Page 100: The Future of Plugin Dev

• TemplatesService is now craft\app\web\View

• include*()→register*() • render()→renderTemplate() • register[Css|Js]Resource()

register asset bundles

Templates

Page 101: The Future of Plugin Dev

• “beginPage”, “endPage”, “beginBody”, and “endBody” events are triggered when rendering templates

Templates

Page 102: The Future of Plugin Dev

‘Types

Page 103: The Future of Plugin Dev

• No more separation of component models and their “type” classes

‘Types

Page 104: The Future of Plugin Dev

Field type architectureCraft 2 Craft 3

Field Model

Field Type

Field Model

Page 105: The Future of Plugin Dev

Widget type architectureCraft 2 Craft 3

Widget Model

Widget Type

Widget Model

Page 106: The Future of Plugin Dev

Task type architectureCraft 2 Craft 3

Task Model

Task Type

Task Model

Page 107: The Future of Plugin Dev

Element type architectureCraft 2 Craft 3

Element Model

Element Type

Element Model

Page 108: The Future of Plugin Dev

‘Type Settings

Page 109: The Future of Plugin Dev

• No more defineSettings() • Settings are now defined directly

on the component classes as public properties

‘Type settings

Page 110: The Future of Plugin Dev

Craft 2 field type example

class XFieldType extends BaseFieldType { protected function defineSettings() { return array( 'maxlen' => AttributeType::Number ); }

// ... }

Page 111: The Future of Plugin Dev

Craft 3 field type example

class X extends Field { public $maxlen;

// ... }

Page 112: The Future of Plugin Dev

Element Queries

Page 113: The Future of Plugin Dev

• ElementCriteriaModel , modifyElementsQuery(), and defineCriteriaAttributes()are dead!

• Replaced with Element Query classes

Element Queries

Page 114: The Future of Plugin Dev

• Elements’ ::find() methods return ElementQuery objects

• ElementQuery handles the query definition and element object population

Element Queries

Page 115: The Future of Plugin Dev

• Element types that need custom query parameters can extend ElementQuery and override their element class’ ::find() method to return their custom element query class

Element Queries

Page 116: The Future of Plugin Dev

• Element queries have protected beforePrepare() and afterPrepare() methods that can be overridden to support custom parameters

Element Queries

Page 117: The Future of Plugin Dev

Element query example

class User extends Element { public static function find() { return new UserQuery( get_called_class() ); }

// ... }

Page 118: The Future of Plugin Dev

Element query example

class UserQuery extends ElementQuery { public $age; protected function beforePrepare() { if ($this->age) { $this->subQuery->andWhere( ['age' => $this->age] ); } return parent::beforePrepare(); } }

Page 119: The Future of Plugin Dev

Plugins

Page 120: The Future of Plugin Dev

• Primary plugin class is no longer necessary

• Plugins are modules • General plugin info now defined

by config.json

Plugins

Page 121: The Future of Plugin Dev

config.json example

{ "name": "Foo", "version": "1.0", "developer": "Pixel & Tonic", "developerUrl": "http://foo.com" }

Page 122: The Future of Plugin Dev

• Plugins are modules • Primary plugin class is optional • General plugin info now defined

in config.json • No more strict class name/folder

requirements

Plugins

Page 123: The Future of Plugin Dev

Installing Plugins

Page 124: The Future of Plugin Dev

• Craft 2 would automatically create/drop tables based on your records on install/uninstall

• Craft 3 leaves it up to the plugin, calling its install() and uninstall() methods

Installing Plugins

Page 125: The Future of Plugin Dev

• By default, install() and uninstall() will check for an “Install” migration within the plugin, and call its safeUp() and safeDown() methods

Installing Plugins

Page 126: The Future of Plugin Dev

• Craft has an InstallMigration class that can be extended

• All you have to do is override its defineSchema() function, defining your plugin’s default table schema.

Installing Plugins

Page 127: The Future of Plugin Dev

Install migration example

class Install extends InstallMigration { protected function defineSchema() { '{{%foo_users}}' => [ 'columns' => [ 'username' => 'string(36)', 'age' => 'integer' ] ] } }

Page 128: The Future of Plugin Dev

• Craft has its own Install migration that the installer runs:craft/app/migrations/Install.php

• Also shows how to add default content to the tables

Installing Plugins

Page 129: The Future of Plugin Dev

Let’s port a plugin

Page 130: The Future of Plugin Dev

Thank you!Try the Craft 3 Dev Preview today at

buildwithcraft.com/3