the future of plugin dev
TRANSCRIPT
THE FUTURE OF PLUGIN DEV
Brandon Kelly
Craft 3 Dev Preview coming in May
Powered by Yii 2
Totally refactored
Faster
Requests/second(bigger is better)
EE Joomla Drupal 7 Craft 2 Craft 3
17.52
9.799.05
4.784.17
Response Times(in milliseconds - smaller is better)
EE Joomla Drupal 7 Craft 2 Craft 3
57
102110
209240
buildwithcraft.com/3
What has changed for Craft has also changed
for plugins
• What’s changed in PHP • What’s changed in Yii • What’s changed in Craft • Port a plugin
Game plan
PHP
Yii 2 and Craft 3 bothrequire PHP 5.4(Yii 1 required 5.1; Craft 2 required 5.3)
• JSON extension • ZIP extension • Upload progress-tracking hooks • DateTime/DateTimeZone
PHP 5.2 features
• Namespaces • Late static bindings • Closures
PHP 5.3 features
Namespaces
namespace craft\plugins\foo;
class Plugin { public function getClassName() { return get_class($this); } }
$plugin = new Plugin(); echo $plugin->getClassName(); // => "craft\plugins\foo\Plugin"
Late static bindings
class Object { public static function className() { return get_called_class(); } }
class Foo extends Object { }
echo Foo::className(); // => "Foo"
Closures
$obj->on('thing', function($event) { // ... });
• Traits • Short array syntax • Function array dereferencing • Class member access on
instantiation
PHP 5.4 Features
Traits
trait FooTrait { public $bar = 'bar'; }
class Foo { use FooTrait; }
$foo = new Foo(); echo $foo->bar; // => "bar"
Short array syntax
$myIndexedArray = [ 'key1' => 1, 'key2' => 2 ];
$myBoringArray = [1, 2, 3];
Function array dereferencing
function foo() { return ['one', 'two', 'three']; }
echo foo()[0]; // => "one"
Class member access on instantiation
class Foo { public function bar() { // ... } }
(new Foo()) ->bar();
Yii
Class Names and Namespaces
• 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
Path alias example
Yii::setAlias('@foo', '/path/to/foo');
echo Yii::getAlias('@foo/subpath'); // => "/path/to/foo/subpath"
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
Objects and Components
• Base class for everything • Property getters/setters • Event handling • Behaviors
CComponent in Yii 1
• yii\base\Object • yii\base\Component
Split into two classes in Yii 2
• The new base class for everything
• Only does propertygetters/setters
Object
• Extends Object • Event handling • Behaviors
Component
• Extend Object for classes that don’t do anything besides represent data (glorified arrays)
• Extend Component for everything else
When to use which?
• yii\base\Event • yii\db\TableSchema • yii\mail\BaseMessage • yii\web\CookieCollection • yii\web\UrlRule
Object examples
• yii\base\Model • yii\db\Connection • yii\i18n\I18N • yii\mail\BaseMailer • yii\web\Request
Component examples
Objects are configurable
• Define object config settings as public properties
• Pass configuration arrays into the object constructor
Configuring objects
Object configuration examples
class User extends Object { public $age; }
$user = new User(['age' => 21]);
Object configuration examples
class User extends Object { public function setAge($age) { // ... } public function getAge() { // ... } }
$user = new User(['age' => 21]);
Object configuration examples
$user = Yii::createObject('foo\User');
$user = Yii::createObject([ 'class' => 'foo\User', 'age' => 21 ]);
Service Locators
• 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
Modules
• Base: yii\base\Module • They are service locators, so they
have sub-components • They also have controllers, views,
and sub-modules
Modules
Applications
• Base: yii\base\Application • They are modules • They handle the request • Only one application per request
Applications
• Web requests:yii\web\Application
• Console requests:yii\console\Application
Two application classes
Controllers
• Web and console applications have controllers (no more “console command” classes)
• Controller action routing works the same either way
• They return the response
Controllers
Request vs. Response
• 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
• 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
• Describes the console command • Requested route • Requested parameters
yii\console\Request
• Defines the HTTP response • Status code (200, 404, etc.) • Headers • Cookies • Body
yii\web\Response
• HTML • XML • JSON • JSONP
Web response formats
Formatted response examples
$response = Yii::$app->getResponse(); $response->data = $user; $response->format = 'json'; $response->send();
{ "id": 5, "username": "admin", "age": 21 }
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>
Events
• 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
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){/*...*/};
• Not explicitly predefined • Attached by calling on() • Raised by calling trigger()
Events in Yii 2
Yii 2 event example
class User extends Component { public function wake() { $this->trigger('afterWake'); } }
$user = new User(); $user->on('afterWake', function($ev){ // ... });
Class-level Event Handlers
Class-level event handler example
Event::on('foo\User', 'afterWake', function($ev) { // ... } );
Asset Bundles
• 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
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' ]; }
Registering an asset bundle
Yii::$app->view->registerAssetBundle( 'foo\AppAsset' );
DB Queries
• 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
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()
SELECT query examples
$user = (new Query()) ->from('{{%users}}') ->where(['id' => 5]) ->one();
SELECT query examples
$users = (new Query()) ->from('{{%users}}') ->where('age >= :min', [ ':min' => 21 ]) ->all();
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()
More query examples
Yii::$app->db->createCommand() ->insert('users', [ 'username' => 'brandon' 'age' => 29 ]) ->execute();
More query examples
Yii::$app->db->createCommand() ->update('users', ['age' => 30], ['id' => 5] ) ->execute();
How they map togetherQuery Execution Method Command Execution Method
all() queryAll()
one() queryOne()
count() sum()
average() min() max()
exists() scalar()
queryScalar()
column() queryColumn()
Active Record
• Base: yii\db\ActiveRecord • Records’ ::find() methods
return ActiveQuery objects • ActiveQuery handles the query
definition and active record object population
Active Record
Active record query example
$users = User::find() ->where('age => :min', [ ':min' => 21 ]) ->all();
Internationalization(I18n)
• 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
Craft
• 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
• Craft 2: craft() returns the Craft\WebApp instance
• Craft 3: Craft::$app returns thecraft\app\web\Application instance
Accessing the Application instance
• 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
• userSession is now user / getUser()
• httpSession is now session / getSession()
Application component name changes
Routes and Controllers
• 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
Craft 2 route example
return array( // Template route: 'foo' => 'path/to/template', // Controller action route: 'bar' => array( 'action' => 'foo/bar' ) );
Craft 3 route example
return [ // Template route: 'foo' => [ 'template' => 'template/path', ], // Controller action route: 'bar' => 'foo/bar' ];
• Params captured by URL rules are now passed directly to the action’s arguments
• URL Manager’s setRouteVariables() is now setRouteParams()
Routes and controllers
Models
• 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
Craft 2 model example
class User extends BaseModel { protected function defineAttributes() { return array( 'id' => AttributeType::Number, 'age' => AttributeType::Number ); } }
Craft 3 model example
class User extends Model { public $id; public $age;
public function rules() { return [ ['id,age', 'number'] ]; } }
Records
• 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
Templates
• TemplatesService is now craft\app\web\View
• include*()→register*() • render()→renderTemplate() • register[Css|Js]Resource()
register asset bundles
Templates
• “beginPage”, “endPage”, “beginBody”, and “endBody” events are triggered when rendering templates
Templates
‘Types
• No more separation of component models and their “type” classes
‘Types
Field type architectureCraft 2 Craft 3
Field Model
Field Type
Field Model
Widget type architectureCraft 2 Craft 3
Widget Model
Widget Type
Widget Model
Task type architectureCraft 2 Craft 3
Task Model
Task Type
Task Model
Element type architectureCraft 2 Craft 3
Element Model
Element Type
Element Model
‘Type Settings
• No more defineSettings() • Settings are now defined directly
on the component classes as public properties
‘Type settings
Craft 2 field type example
class XFieldType extends BaseFieldType { protected function defineSettings() { return array( 'maxlen' => AttributeType::Number ); }
// ... }
Craft 3 field type example
class X extends Field { public $maxlen;
// ... }
Element Queries
• ElementCriteriaModel , modifyElementsQuery(), and defineCriteriaAttributes()are dead!
• Replaced with Element Query classes
Element Queries
• Elements’ ::find() methods return ElementQuery objects
• ElementQuery handles the query definition and element object population
Element Queries
• 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
• Element queries have protected beforePrepare() and afterPrepare() methods that can be overridden to support custom parameters
Element Queries
Element query example
class User extends Element { public static function find() { return new UserQuery( get_called_class() ); }
// ... }
Element query example
class UserQuery extends ElementQuery { public $age; protected function beforePrepare() { if ($this->age) { $this->subQuery->andWhere( ['age' => $this->age] ); } return parent::beforePrepare(); } }
Plugins
• Primary plugin class is no longer necessary
• Plugins are modules • General plugin info now defined
by config.json
Plugins
config.json example
{ "name": "Foo", "version": "1.0", "developer": "Pixel & Tonic", "developerUrl": "http://foo.com" }
• Plugins are modules • Primary plugin class is optional • General plugin info now defined
in config.json • No more strict class name/folder
requirements
Plugins
Installing Plugins
• 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
• By default, install() and uninstall() will check for an “Install” migration within the plugin, and call its safeUp() and safeDown() methods
Installing Plugins
• 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
Install migration example
class Install extends InstallMigration { protected function defineSchema() { '{{%foo_users}}' => [ 'columns' => [ 'username' => 'string(36)', 'age' => 'integer' ] ] } }
• 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
Let’s port a plugin
Thank you!Try the Craft 3 Dev Preview today at
buildwithcraft.com/3