Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

1. Security & Performance

Slide 3

Slide 3 text

Modern Tech Stack — PHP 7 — Yii 2 — Guzzle 6 — Twig 2 — OpenSSL

Slide 4

Slide 4 text

Average time (milliseconds) taken to load the Happy Lager homepage over 100 requests

Slide 5

Slide 5 text

Seconds taken to load 5,000 entries at once

Slide 6

Slide 6 text

Memory (MB) consumed when loading 5,000 entries at once

Slide 7

Slide 7 text

2. Composer

Slide 8

Slide 8 text

Craft is now a Composer dependency right alongside other project dependencies.

Slide 9

Slide 9 text

Craft is now a Composer dependency right alongside other project dependencies. > composer require \ craftcms/cms:^3.0.0-beta.1

Slide 10

Slide 10 text

There’s also a Composer project you can use as a starting point. > composer create-project \ craftcms/craft \ my_project \ --stability beta

Slide 11

Slide 11 text

The default project structure has changed a bit. Old New craft/app/ vendor/craftcms/cms/ craft/config/ config/ craft/storage/ storage/ craft/templates/ templates/ public/ web/

Slide 12

Slide 12 text

Craft is on GitHub.

Slide 13

Slide 13 text

Craft is on GitHub.

Slide 14

Slide 14 text

Live on the bleeding edge. > composer require \ craftcms/cms:"dev-develop as 3.0.0-beta.99"

Slide 15

Slide 15 text

3. Multi-Site

Slide 16

Slide 16 text

Sites are like locales, except… — you give them a custom name and handle — they have language, but they are not defined by it — multiple sites can share the same language — fields can be translated per-site, per-language, or even something custom

Slide 17

Slide 17 text

When should you use Multi-Site? ! " Multi-lingual sites Different orgs Sister sites No shared content Landing pages

Slide 18

Slide 18 text

4. Internationalization

Slide 19

Slide 19 text

Internationalization improvements in Craft 3: — Craft uses the Intl extension when available. — Fallback data for all the locales is available at github.com/craftcms/locales — Support for PostgreSQL ! — Translation categories

Slide 20

Slide 20 text

5. Assets

Slide 21

Slide 21 text

Image Editor

Slide 22

Slide 22 text

Image Editor

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Volumes — Asset Sources are called “Volumes” now — Plugins can supply custom volume types — User photos are stored as assets now, and you choose which volume

Slide 25

Slide 25 text

6. Utilities

Slide 26

Slide 26 text

6. Utilities

Slide 27

Slide 27 text

6. Utilities

Slide 28

Slide 28 text

6. Utilities

Slide 29

Slide 29 text

6. Utilities

Slide 30

Slide 30 text

6. Utilities

Slide 31

Slide 31 text

6. Utilities

Slide 32

Slide 32 text

6. Utilities

Slide 33

Slide 33 text

6. Utilities

Slide 34

Slide 34 text

6. Utilities

Slide 35

Slide 35 text

7. Content Migrations

Slide 36

Slide 36 text

Craft can now manage migrations that are specific to your project. > ./craft migrate/create create_new_field

Slide 37

Slide 37 text

Content migrations are stored in a migrations/ folder within your Craft project.

Slide 38

Slide 38 text

use craft\db\Migration; use craft\fields\PlainText; class m170331_030925_create_new_field extends Migration { public function safeUp() { $field = new PlainText(); $field->groupId = 1; $field->name = 'Address'; $field->handle = 'address'; $field->multiline = true; \Craft::$app->fields->saveField($field); } }

Slide 39

Slide 39 text

Content migrations have access to the full set of Craft APIs, so you can do whatever you want in them.

Slide 40

Slide 40 text

8. Templating

Slide 41

Slide 41 text

Element Queries

Slide 42

Slide 42 text

ElementQuery is the new ElementCriteriaModel.

Slide 43

Slide 43 text

ElementQuery is the new ElementCriteriaModel. Still works the same, for the most part. {% set entries = craft.entries() .section('news') .limit(10) %}

Slide 44

Slide 44 text

ElementQuery is the new ElementCriteriaModel. Still works the same, for the most part. {% set entries = craft.entries() .section('news') .limit(10) %} But way, way more powerful.

Slide 45

Slide 45 text

A few functions have changed. Old New find() all() first() one() order() orderBy() total() count()

Slide 46

Slide 46 text

Lots of new functions have been introduced. Name Purpose select(columns) override the SELECT addSelect(columns) add more columns to the SELECT

Slide 47

Slide 47 text

Lots of new functions have been introduced. Name Purpose where(condition) override the WHERE andWhere(condition) add an AND condition to the WHERE orWhere(condition) add an OR condition to the WHERE

Slide 48

Slide 48 text

Lots of new functions have been introduced. Name Purpose innerJoin(table) add an INNER JOIN leftJoin(table) add a LEFT JOIN rightJoin(table) add a RIGHT JOIN

Slide 49

Slide 49 text

Lots of new functions have been introduced. Name Purpose asArray() return raw data arrays rather than Element models indexBy(column) indexes returned values by a column’s value

Slide 50

Slide 50 text

Lots of new functions have been introduced. Name Returns column() first column’s values pairs() key/value pairs scalar() first value of first column exists() if there are any results

Slide 51

Slide 51 text

Lots of new functions have been introduced. Name Returns sum(column) sum value average(column) average value min(column) smallest value max(column) largest value

Slide 52

Slide 52 text

Lots of new functions have been introduced. Name Returns batch(size) lazy-loaded batched results each(size) iterator for all results, but lazy-loaded in batches behind the scenes

Slide 53

Slide 53 text

Example: get entries, indexed by their IDs {% set entriesById = craft.entries() .section('news') .indexBy('elements.id') .all() %}

Slide 54

Slide 54 text

Example: get entry titles, indexed by their IDs {% set titlesById = craft.entries() .section('news') .select('elements.id, content.title') .pairs() %}

Slide 55

Slide 55 text

Example: get the average value for a custom field {% set averageAge = craft.users() .andWhere('field_age is not null') .average('field_age') %}

Slide 56

Slide 56 text

Example: load users in lazy-loaded batches {% for user in craft.users().each() %}
  • {{ user.name }}
  • {% endfor %}

    Slide 57

    Slide 57 text

    You can output the raw SQL query to be executed: {% set q = craft.entries().section('news') %}
    {{ q.getRawSql() }}

    Slide 58

    Slide 58 text

    You can output the raw SQL query to be executed: {% set q = craft.entries().section('news') %}
    {{ q.getRawSql() }}
    Output: SELECT `elements`.`id`, `elements`.`fieldLayoutId`, `elements`.`uid`, ...

    Slide 59

    Slide 59 text

    cra!.app

    Slide 60

    Slide 60 text

    Templates can access Craft’s application instance anywhere with craft.app.

    Slide 61

    Slide 61 text

    Example: access a config value {% if craft.app.config.general.devMode %}

    Craft is running in Dev Mode!

    {% endif %}

    Slide 62

    Slide 62 text

    Example: loop through all the sections {% set sections = craft.app .sections.getAllSections() %} {% for section in sections %}
  • {{ section.name }}
  • {% endfor %}

    Slide 63

    Slide 63 text

    9. Debug Toolbar

    Slide 64

    Slide 64 text

    No content

    Slide 65

    Slide 65 text

    No content

    Slide 66

    Slide 66 text

    No content

    Slide 67

    Slide 67 text

    No content

    Slide 68

    Slide 68 text

    No content

    Slide 69

    Slide 69 text

    No content

    Slide 70

    Slide 70 text

    No content

    Slide 71

    Slide 71 text

    No content

    Slide 72

    Slide 72 text

    No content

    Slide 73

    Slide 73 text

    No content

    Slide 74

    Slide 74 text

    No content

    Slide 75

    Slide 75 text

    10. Configuration

    Slide 76

    Slide 76 text

    Define environment-specific variables in a .env file that doesn’t get committed to Git. DB_SERVER="localhost" DB_USER="homestead" DB_PASSWORD="secret" DB_DATABASE="happylager"

    Slide 77

    Slide 77 text

    Ways to define environment-specific settings: — Per-environment configs — PHP dotenv environment variables — environmentVariables

    Slide 78

    Slide 78 text

    Ways to define environment-specific settings: — Per-environment configs — PHP dotenv environment variables — environmentVariables

    Slide 79

    Slide 79 text

    Site URLs can be defined per-environment using the siteUrl config setting in config/general.php. return [ '*' => [], '.dev' => [ 'siteUrl' => 'http://site.dev', ], '.com' => [ 'siteUrl' => '//site.com', ], ], ];

    Slide 80

    Slide 80 text

    Volume settings can be overridden in config/volumes.php. return [ 'userPhotos' => [ 'path' => getenv('PHOTOS_PATH'), 'url' => getenv('PHOTOS_URL'), ], ];

    Slide 81

    Slide 81 text

    All plugin settings can be overridden with config files now.

    Slide 82

    Slide 82 text

    You can set default Guzzle settings from config/guzzle.php.

    Slide 83

    Slide 83 text

    return [ 'headers' => ['Foo' => 'Bar'], 'query' => ['testing' => '123'], 'auth' => ['username', 'password'], 'proxy' => 'tcp://localhost:80' ];

    Slide 84

    Slide 84 text

    You can override Craft’s core application configuration with config/app.php.

    Slide 85

    Slide 85 text

    return [ 'components' => [ 'mailer' => function() { $settings = Craft::$app->systemSettings->emailSettings; $settings->transportType = MailgunAdapter::class; $settings->transportSettings = [ 'domain' => 'foo.com', 'apiKey' => 'key-xxxxxxxxxx', ]; return MailerHelper::createMailer($settings); } ], ];

    Slide 86

    Slide 86 text

    Recap 1. Security & Performance 2. Composer 3. Multi-Site 4. Internationalization 5. Assets 6. Utilities 7. Content Migrations 8. Templating 9. Debug Toolbar 10. Configuration