Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Three Goals for Cra! 3 1. Establish new coding guidelines to improve performance, stability, and maintainability 2. Support modern dev toolkits 3. Stop reinventing the wheel

Slide 3

Slide 3 text

Big Changes — Built on Yii 2 — Requires PHP 7 — PostgreSQL support — Tons of refactoring — More extensible

Slide 4

Slide 4 text

Yii 2

Slide 5

Slide 5 text

Yii 2 — Complete rewrite — Code style based on PSR-1 and PSR-2 specs — Namespaces and class autoloading based on PSR-4 spec — Console requests are a first class citizen now — Asset bundles — Query builder

Slide 6

Slide 6 text

PSR-4 Namespaces and Autoloading — Each package gets a root namespace that maps to a source directory — Craft’s is craft\ — 1st party plugins use craft\something\ — 3rd parties should use dev_name\something\ — Class namespaces reflect where they live — Class names reflect their filename

Slide 7

Slide 7 text

PSR-4 Namespaces and Autoloading Class Location in vendor/ craft\web\View cra!cms/cms/src/web/ View.php craft\awss3\Volume cra!cms/aws-s3/src/ Volume.php marionnewlevant\snitch\ Plugin marionnewlevant/snitch/ src/Plugin.php

Slide 8

Slide 8 text

Web vs. Console Requests Thing Return value for web requests Craft::$app craft\web\ Application ->request craft\web\Request ->isConsoleRequest false

Slide 9

Slide 9 text

Web vs. Console Requests Thing Return value for console requests Craft::$app craft\console\ Application ->request craft\console\Request ->isConsoleRequest true

Slide 10

Slide 10 text

Asset Bundles — Classes that list CSS and JavaScript dependencies, which can be registered to the View when needed — They can list other asset bundles as dependencies, too — Registered asset bundles get published into a public directory, so they can be served statically

Slide 11

Slide 11 text

Query Builder — All SELECT queries are now built from yii\db\Query objects (even Active Record queries and Element queries) — Lots of handy execution functions — DB-agnostic

Slide 12

Slide 12 text

Normal query example: $admins = (new \craft\db\Query()) ->from('{{%users}} u') ->innerJoin('{{%elements}} e', '[[e.id]]=[[u.id]]') ->where(['admin' => 1]) ->all();

Slide 13

Slide 13 text

Active Record query example: $admins = \craft\records\User::find() ->with('element') ->where(['admin' => 1]) ->all();

Slide 14

Slide 14 text

Element Query example: $admins = \craft\elements\User::find() ->admin() ->all();

Slide 15

Slide 15 text

PHP 7

Slide 16

Slide 16 text

Main New Features Since PHP 5.3 — Traits (5.4) — ['a' => 1] short array syntax (5.4) — ::class keyword (5.5) — Scalar argument type declarations (7.0) — Return type declarations (7.0) — ?? “null coalescing operator” (7.0)

Slide 17

Slide 17 text

Refactoring

Slide 18

Slide 18 text

Component Refactor — In Craft 2, components are represented by two objects: the model and the ’type — The model stores common attributes (ID, name, handle, etc.) — The ’type handles all type-specific functionality (settings, view HTML, etc.)

Slide 19

Slide 19 text

Component Refactor — In Craft 3, components are represented by a single model — Common attributes and default functionality are defined in a base class — Type-specific attributes/functionality is handled by subclasses

Slide 20

Slide 20 text

Component Refactor — Interface-oriented APIs — ElementInterface, ElementActionInterface, FieldInterface, and WidgetInterface — Common attributes provided by traits — ElementTrait, FieldTrait, and WidgetTrait

Slide 21

Slide 21 text

Example: the Feed widget — craft\base\WidgetInterface defines required widget methods — craft\base\WidgetTrait defines required widget properties — craft\base\Widget implements them as much as it can, providing some default functionality — craft\widgets\Feed extends the base class, filling in the gaps and overriding default assumptions when needed

Slide 22

Slide 22 text

Speed Boost — Stop using magic getters and setters — Class autoloading instead of file scanning — Explicit plugin service registration — Event-based ’type registration — Events instead of hooks — Optimized element queries

Slide 23

Slide 23 text

How we’ve avoided magic getters and setters: — No more defineAttributes() for model attributes; use public properties instead — Config settings stored on class properties now (GeneralConfig, DbConfig, etc.) — Custom field values stored on public properties of dynamically-generated ContentBehavior

Slide 24

Slide 24 text

“Event-based component registration” example…

Slide 25

Slide 25 text

use yii\base\Event; class Plugin extends \craft\base\Plugin { public function init() { Event::on(/*class*/, /*event*/, /*callback*/); } }

Slide 26

Slide 26 text

use yii\base\Event; use craft\services\Fields; class Plugin extends \craft\base\Plugin { public function init() { Event::on(Fields::class, /*event*/, /*callback*/); } }

Slide 27

Slide 27 text

use yii\base\Event; use craft\services\Fields; class Plugin extends \craft\base\Plugin { public function init() { Event::on(Fields::class, Fields::EVENT_REGISTER_FIELD_TYPES, /*callback*/); } }

Slide 28

Slide 28 text

use yii\base\Event; use craft\services\Fields; use craft\events\RegisterComponentTypesEvent; class Plugin extends \craft\base\Plugin { public function init() { Event::on(Fields::class, Fields::EVENT_REGISTER_FIELD_TYPES, function(RegisterComponentTypesEvent $e) { $e->types[] = MyFieldType::class; }); } }

Slide 29

Slide 29 text

How we optimized element queries: — All Conditions applied in a sub-query — Ordering and limiting applied in the sub-query — All the actual returned data is selected by the main query

Slide 30

Slide 30 text

SELECT elements.id, elements.fieldLayoutId, elements.uid, elements.enabled, elements.archived, elements.dateCreated, elements.dateUpdated, elements_i18n.slug, elements_i18n.uri, elements_i18n.enabled AS enabledForSite, entries.sectionId, entries.typeId, entries.authorId, entries.postDate, entries.expiryDate, content.id AS contentId, content.title, content.field_body FROM ( SELECT elements.id AS elementsId, elements_i18n.id AS elementsI18nId, content.id AS contentId FROM elements INNER JOIN entries ON entries.id = elements.id INNER JOIN elements_i18n ON elements_i18n.elementId = elements.id INNER JOIN content ON content.elementId = elements.id WHERE elements_i18n.siteId=1 AND content.siteId=1 AND elements_i18n.enabled='1' ORDER BY postDate DESC LIMIT 100 ) subquery INNER JOIN entries ON entries.id = subquery.elementsId INNER JOIN elements ON elements.id = subquery.elementsId INNER JOIN elements_i18n ON elements_i18n.id = subquery.elementsI18nId INNER JOIN content ON content.id = subquery.contentId ORDER BY postDate DESC

Slide 31

Slide 31 text

Stability and Maintainability — Doc blocks for IDE code completion — Declare argument/return types when possible — Call getter/setter methods directly rather than going through magic __get() and __set() — Clarity over brevity/cleverness — Utilize PhpStorm inspections and Scrutinizer

Slide 32

Slide 32 text

Php Inspections Plugin

Slide 33

Slide 33 text

Yii2 Inspections Plugin

Slide 34

Slide 34 text

github.com/craftcms/phpstorm-settings

Slide 35

Slide 35 text

Extensibility Improvements

Slide 36

Slide 36 text

Composer — No more plugin dependency conflicts — Plugins can share custom helper classes, etc, with packages

Slide 37

Slide 37 text

Example: Vue.js Asset Bundle

Slide 38

Slide 38 text

New Component Types — Volumes — Utilities — Mailer adapters

Slide 39

Slide 39 text

Guzzle 6 + PSR-7 — Create PSR-7-compliant HTTP clients with Craft::createGuzzleClient() — Configured from config/guzzle.php — Used when sending support requests, checking for updates, fetching RSS feeds — Amazon S3 and Mailgun plugins use it

Slide 40

Slide 40 text

Plugins

Slide 41

Slide 41 text

Plugins are an extension of Yii Modules in Craft 3.

Slide 42

Slide 42 text

Modules… — are sub-applications of the main app — have their own application components/services — have their own web/console controllers — can be nested

Slide 43

Slide 43 text

Plugins extend Modules with… — the concept of installation/uninstallation — their own DB migration tracks — their own translation category

Slide 44

Slide 44 text

The training wheels are off. — No more automatic table creation/deletion on install/ uninstall — No more automatic service registration — No more automatic component registration

Slide 45

Slide 45 text

Plugins aren’t always needed anymore. — Bootstrap custom modules in config/app.php — Event-based route definition (craft\web\UrlManager::EVENT_REGISTER_CP_URL_RULES) — Use Content Migrations to make DB changes — Event-based component registration

Slide 46

Slide 46 text

Definitely make it a plugin if… — you want to make it available in the Plugin Store — you could use the ability to install/uninstall

Slide 47

Slide 47 text

Maybe skip the plugin if… — it’s just for this one project — it’s a shared library that is really just a dependency of actual plugins