MOON
Server: Apache
System: Linux res.emeff.ca 3.10.0-962.3.2.lve1.5.24.10.el7.x86_64 #1 SMP Wed Mar 20 07:36:02 EDT 2019 x86_64
User: accemeff (1004)
PHP: 7.0.33
Disabled: NONE
Upload Files
File: /home/accemeff/vendor/craftcms/cms/docs/changes-in-craft-3.md
# Changes in Craft 3

[[toc]]

## Rich Text Fields

The “Rich Text” field type has been removed from Craft 3, in favor of new [Redactor](https://github.com/craftcms/redactor) and [CKEditor](https://github.com/craftcms/ckeditor) plugins.

If you have any existing Rich Text fields, they will be automatically converted to Redactor fields when you install the Redactor plugin.

### Redactor Configs

If you do install the Redactor plugin, you will need to ensure that your Redactor configs in `config/redactor/` are valid JSON. That means:

- No comments
- All object properties (the config setting names) must be wrapped in double quotes
- All strings must use double quotes rather than single quotes

```javascript
// Bad:
{
  /* interesting comment */
  buttons: ['bold', 'italic']
}

// Good:
{
  "buttons": ["bold", "italic"]
}
```

## Position Select Fields

The “Position Select” field type has been removed from Craft 3. If you had any Position Select fields, they will be converted to Dropdown fields, with all the same options.

If you miss Position Select, you can try installing the [Position Fieldtype](https://github.com/Rias500/craft-position-fieldtype) plugin, which brings it back.

## Remote Volumes

Support for Amazon S3, Rackspace Cloud Files, and Google Cloud Storage have been moved into plugins. If you have any asset volumes that were using those services in Craft 2, you will need to install the new plugins:

- [Amazon S3](https://github.com/craftcms/aws-s3)
- [Rackspace Cloud Files](https://github.com/craftcms/rackspace)
- [Google Cloud Storage](https://github.com/craftcms/google-cloud)

## Configuration

### Config Settings

The following config settings have been deprecated in Craft 3, and will be completely removed in Craft 4:

| File          | Old Setting                  | New Setting
| ------------- | ---------------------------- | -----------------------------
| `general.php` | `activateAccountFailurePath` | `invalidUserTokenPath`
| `general.php` | `backupDbOnUpdate`           | `backupOnUpdate`<sup>1</sup>
| `general.php` | `defaultFilePermissions`     | `defaultFileMode`<sup>2</sup>
| `general.php` | `defaultFolderPermissions`   | `defaultDirMode`
| `general.php` | `environmentVariables`       | `aliases` <sup>3</sup>
| `general.php` | `restoreDbOnUpdateFailure`   | `restoreOnUpdateFailure`
| `general.php` | `useWriteFileLock`           | `useFileLocks`
| `general.php` | `validationKey`              | `securityKey`<sup>4</sup>

*<sup>1</sup> Performance should no longer be a major factor when setting `backupOnUpdate` to `false`, since backups aren’t generated by PHP anymore.*

*<sup>2</sup> `defaultFileMode` is now `null` by default, meaning it will be determined by the current environment.*

*<sup>3</sup> Settings that supported Environment Variables in Craft 2 now support [Aliases](https://www.yiiframework.com/doc/guide/2.0/en/concept-aliases) in Craft 3. Site URL and Local volume settings will automatically be converted to the new Alias syntax when updating to Craft 3 (`@variable` instead of `{variable}`).

*<sup>4</sup> `securityKey` is no longer optional. If you haven’t set it yet, set it to the value in `storage/runtime/validation.key` (if the file exists). The auto-generated `validation.key` file fallback will be removed in Craft 4.*

The following config settings have been removed entirely:

| File          | Setting
| ------------- | -----------
| `db.php`      | `collation`
| `db.php`      | `initSQLs`
| `general.php` | `appId`
| `general.php` | `cacheMethod` (see [Configuration → Data Caching Config](config/README.md#data-caching-config))

### `omitScriptNameInUrls` and `usePathInfo`

The `omitScriptNameInUrls` setting can no longer be set to `'auto'`, as it was by default in Craft 2. Which means you will need to explicitly set it to `true` in `config/general.php` if you’ve configured your server to route HTTP requests to `index.php`.

Similarly, the `usePathInfo` setting can no longer be set to `'auto'` either. If your server is configured to support [PATH_INFO](https://craftcms.com/support/enable-path-info), you can set this to `true`. This is only necessary if you can’t set `omitScriptNameInUrls` to `true`, though.

## URL Rules

If you have any [URL rules](config/README.md#url-rules) saved in `config/routes.php`, you will need to update them to Yii 2’s [pattern-route syntax](https://www.yiiframework.com/doc/guide/2.0/en/runtime-routing#url-rules).

- Named parameters in the pattern should be defined using the format (`<ParamName:RegExp>`) rather than as a regular expression subpattern (`(?P<ParamName>RegExp)`).
- Unnamed parameters are no longer allowed (e.g. `([^\/]+)`). They must also be converted to the new named parameter syntax (`<ParamName:RegExp>`).
- Controller action routes should be defined as a string (`'action/path'`) rather than an array with an `action` key (`['action' => 'action/path']`).
- Template routes should be defined as as an array with a `template` key (`['template' => 'template/path']`) rather than a string (`'template/path'`).

```php
// Old:
'dashboard' => ['action' => 'dashboard/index'],
'settings/fields/new' => 'settings/fields/_edit',
'settings/fields/edit/(?P<fieldId>\d+)' => 'settings/fields/_edit',
'blog/type/([^\/]+)' => 'blog/_type',

// New:
'dashboard' => 'dashboard/index',
'settings/fields/new' => ['template' => 'settings/fields/_edit'],
'settings/fields/edit/<fieldId:\d+>' => ['template' => 'settings/fields/_edit'],
'blog/type/<type:[^\/]+>' => ['template' => 'blog/_type'],
```

## PHP Constants

The following PHP constants have been deprecated in Craft 3, and will no longer work in Craft 4:

| Old              | New
| ---------------- | ----------------------------------------
| `CRAFT_LOCALE`   | [CRAFT_SITE](config/php-constants.md#craft-site)
| `CRAFT_SITE_URL` | Use the <config:siteUrl> config setting instead

## Static Translation Files

Craft 3 still supports [static message translations](static-translations.md), but the directory structure has changed. Now within your `translations/` folder, you should create subdirectories for each locale, and within them, PHP files for each **translation category**.

The acceptable translation categories are:

| Category        | Description
| --------------- | -----------------------------------------
| `app`           | Craft’s translation messages
| `yii`           | Yii’s translation messages
| `site`          | custom site-specific translation messages
| `plugin-handle` | Plugins’ translation messages

In Craft 3, your `translations/` folder might look something like this:

```
translations/
└── de/
    ├── app.php
    └── site.php
```

## User Photos

User photos are stored as assets now. When upgrading to Craft 3, Craft will automatically create a new asset volume called “User Photos” at `storage/userphotos/` (where Craft previously stored all user photos, but without the `<Username>/` subfolders). However this folder is above your web root and inaccessible to HTTP requests, so until you make this volume publicly accessible, user photos will not work on the front end.

Here’s how you can resolve this:

1. Move the `storage/userphotos/` folder somewhere below your web root (e.g. `web/userphotos/`)
2. Go to Settings → Assets → Volumes → User Photos and configure the volume based on the new folder location:
    - Update the File System Path setting to point to the new folder location
    - Enable the “Assets in this volume have public URLs” setting
    - Set the correct URL setting for the folder
    - Save the volume

## Twig 2

Craft 3 uses Twig 2, which has its own breaking changes for templates:

### Macros

Twig 2 requires that you explicitly import macros in each template where you are using them. They are no longer automatically available if a parent template is including them, or even if they were defined in the same template file.

```twig
Old:
{% macro foo %}...{% endmacro %}
{{ _self.foo() }}

New:
{% macro foo %}...{% endmacro %}
{% import _self as macros %}
{{ macros.foo() }}
```

### Undefined Blocks

Twig 1 let you call `block()` even for blocks that didn’t exist:

```twig
{% if block('foo') is not empty %}
    {{ block('foo') }}
{% endif %}
```

Twig 2 will throw an error unless it’s a `defined` test:

```twig
{% if block('foo') is defined %}
    {{ block('foo') }}
{% endif %}
```

## Template Tags

The following Twig template tags have been removed:

| Old                 | New
| ------------------- | -----------------------------------------
| `{% endpaginate %}` | *(no replacement needed; just delete it)*

The following Twig template tags have been deprecated in Craft 3, and will be completely removed in Craft 4:

| Old                             | New
| ------------------------------- | ---------------------------------------------
| `{% includecss %}`              | `{% css %}`
| `{% includehirescss %}`         | `{% css %}` *(write your own media selector)*
| `{% includejs %}`               | `{% js %}`
| `{% includecssfile url %}`      | `{% do view.registerCssFile(url) %}`
| `{% includejsfile url %}`       | `{% do view.registerJsFile(url) %}`
| `{% includecssresource path %}` | See [Asset Bundles](extend/asset-bundles.md)
| `{% includejsresource path %}`  | See [Asset Bundles](extend/asset-bundles.md)

## Template Functions

The following template functions have been removed:

| Old                                         | New
| ------------------------------------------- | ------------------------------------
| `craft.hasPackage()`                        | *(n/a)*
| `craft.entryRevisions.getDraftByOffset()`   | *(n/a)*
| `craft.entryRevisions.getVersionByOffset()` | *(n/a)*
| `craft.fields.getFieldType(type)`           | `craft.app.fields.createField(type)`
| `craft.fields.populateFieldType()`          | *(n/a)*
| `craft.tasks.areTasksPending()`             | `craft.app.queue.getHasWaitingJobs()`<sup>1</sup>
| `craft.tasks.getRunningTask()`              | *(n/a)*
| `craft.tasks.getTotalTasks()`               | *(n/a)*
| `craft.tasks.haveTasksFailed()`             | *(n/a)*
| `craft.tasks.isTaskRunning()`               | `craft.app.queue.getHasReservedJobs()`<sup>1</sup>

*<sup>1</sup> Only available if the `queue` component implements <api:craft\queue\QueueInterface>.*

The following template functions have been deprecated in Craft 3, and will be completely removed in Craft 4:

| Old                                                     | New
| ------------------------------------------------------- | ---------------------------------------------
| `round(num)`                                            | `num|round`
| `getCsrfInput()`                                        | `csrfInput()`
| `getHeadHtml()`                                         | `head()`
| `getFootHtml()`                                         | `endBody()`
| `getTranslations()`                                     | `view.getTranslations()|json_encode|raw`
| `craft.categoryGroups.getAllGroupIds()`                 | `craft.app.categories.allGroupIds`
| `craft.categoryGroups.getEditableGroupIds()`            | `craft.app.categories.editableGroupIds`
| `craft.categoryGroups.getAllGroups()`                   | `craft.app.categories.allGroups`
| `craft.categoryGroups.getEditableGroups()`              | `craft.app.categories.editableGroups`
| `craft.categoryGroups.getTotalGroups()`                 | `craft.app.categories.totalGroups`
| `craft.categoryGroups.getGroupById(id)`                 | `craft.app.categories.getGroupById(id)`
| `craft.categoryGroups.getGroupByHandle(handle)`         | `craft.app.categories.getGroupByHandle(handle)`
| `craft.config.[setting]` *(magic getter)*               | `craft.app.config.general.[setting]`
| `craft.config.get(setting)`                             | `craft.app.config.general.[setting]`
| `craft.config.usePathInfo()`                            | `craft.app.config.general.usePathInfo`
| `craft.config.omitScriptNameInUrls()`                   | `craft.app.config.general.omitScriptNameInUrls`
| `craft.config.getResourceTrigger()`                     | `craft.app.config.general.resourceTrigger`
| `craft.locale()`                                        | `craft.app.language`
| `craft.isLocalized()`                                   | `craft.app.isMultiSite`
| `craft.deprecator.getTotalLogs()`                       | `craft.app.deprecator.totalLogs`
| `craft.elementIndexes.getSources()`                     | `craft.app.elementIndexes.sources`
| `craft.emailMessages.getAllMessages()`                  | `craft.emailMessages.allMessages`
| `craft.emailMessages.getMessage(key)`                   | `craft.app.emailMessages.getMessage(key)`
| `craft.entryRevisions.getDraftsByEntryId(id)`           | `craft.app.entryRevisions.getDraftsByEntryId(id)`
| `craft.entryRevisions.getEditableDraftsByEntryId(id)`   | `craft.entryRevisions.getEditableDraftsByEntryId(id)`
| `craft.entryRevisions.getDraftById(id)`                 | `craft.app.entryRevisions.getDraftById(id)`
| `craft.entryRevisions.getVersionsByEntryId(id)`         | `craft.app.entryRevisions.getVersionsByEntryId(id)`
| `craft.entryRevisions.getVersionById(id)`               | `craft.app.entryRevisions.getVersionById(id)`
| `craft.feeds.getFeedItems(url)`                         | `craft.app.feeds.getFeedItems(url)`
| `craft.fields.getAllGroups()`                           | `craft.app.fields.allGroups`
| `craft.fields.getGroupById(id)`                         | `craft.app.fields.getGroupById(id)`
| `craft.fields.getFieldById(id)`                         | `craft.app.fields.getFieldById(id)`
| `craft.fields.getFieldByHandle(handle)`                 | `craft.app.fields.getFieldByHandle(handle)`
| `craft.fields.getAllFields()`                           | `craft.app.fields.allFields`
| `craft.fields.getFieldsByGroupId(id)`                   | `craft.app.fields.getFieldsByGroupId(id)`
| `craft.fields.getLayoutById(id)`                        | `craft.app.fields.getLayoutById(id)`
| `craft.fields.getLayoutByType(type)`                    | `craft.app.fields.getLayoutByType(type)`
| `craft.fields.getAllFieldTypes()`                       | `craft.app.fields.allFieldTypes`
| `craft.globals.getAllSets()`                            | `craft.app.globals.allSets`
| `craft.globals.getEditableSets()`                       | `craft.app.globals.editableSets`
| `craft.globals.getTotalSets()`                          | `craft.app.globals.totalSets`
| `craft.globals.getTotalEditableSets()`                  | `craft.app.globals.totalEditableSets`
| `craft.globals.getSetById(id)`                          | `craft.app.globals.getSetById(id)`
| `craft.globals.getSetByHandle(handle)`                  | `craft.app.globals.getSetByHandle(handle)`
| `craft.i18n.getAllLocales()`                            | `craft.app.i18n.allLocales`
| `craft.i18n.getAppLocales()`                            | `craft.app.i18n.appLocales`
| `craft.i18n.getCurrentLocale()`                         | `craft.app.locale`
| `craft.i18n.getLocaleById(id)`                          | `craft.app.i18n.getLocaleById(id)`
| `craft.i18n.getSiteLocales()`                           | `craft.app.i18n.siteLocales`
| `craft.i18n.getSiteLocaleIds()`                         | `craft.app.i18n.siteLocaleIds`
| `craft.i18n.getPrimarySiteLocale()`                     | `craft.app.i18n.primarySiteLocale`
| `craft.i18n.getEditableLocales()`                       | `craft.app.i18n.editableLocales`
| `craft.i18n.getEditableLocaleIds()`                     | `craft.app.i18n.editableLocaleIds`
| `craft.i18n.getLocaleData()`                            | `craft.app.i18n.getLocaleById(id)`
| `craft.i18n.getDatepickerJsFormat()`                    | `craft.app.locale.getDateFormat('short', 'jui')`
| `craft.i18n.getTimepickerJsFormat()`                    | `craft.app.locale.getTimeFormat('short', 'php')`
| `craft.request.isGet()`                                 | `craft.app.request.isGet`
| `craft.request.isPost()`                                | `craft.app.request.isPost`
| `craft.request.isDelete()`                              | `craft.app.request.isDelete`
| `craft.request.isPut()`                                 | `craft.app.request.isPut`
| `craft.request.isAjax()`                                | `craft.app.request.isAjax`
| `craft.request.isSecure()`                              | `craft.app.request.isSecureConnection`
| `craft.request.isLivePreview()`                         | `craft.app.request.isLivePreview`
| `craft.request.getScriptName()`                         | `craft.app.request.scriptFilename`
| `craft.request.getPath()`                               | `craft.app.request.pathInfo`
| `craft.request.getUrl()`                                | `url(craft.app.request.pathInfo)`
| `craft.request.getSegments()`                           | `craft.app.request.segments`
| `craft.request.getSegment(num)`                         | `craft.app.request.getSegment(num)`
| `craft.request.getFirstSegment()`                       | `craft.app.request.segments|first`
| `craft.request.getLastSegment()`                        | `craft.app.request.segments|last`
| `craft.request.getParam(name)`                          | `craft.app.request.getParam(name)`
| `craft.request.getQuery(name)`                          | `craft.app.request.getQueryParam(name)`
| `craft.request.getPost(name)`                           | `craft.app.request.getBodyParam(name)`
| `craft.request.getCookie(name)`                         | `craft.app.request.cookies.get(name)`
| `craft.request.getServerName()`                         | `craft.app.request.serverName`
| `craft.request.getUrlFormat()`                          | `craft.app.config.general.usePathInfo`
| `craft.request.isMobileBrowser()`                       | `craft.app.request.isMobileBrowser()`
| `craft.request.getPageNum()`                            | `craft.app.request.pageNum`
| `craft.request.getHostInfo()`                           | `craft.app.request.hostInfo`
| `craft.request.getScriptUrl()`                          | `craft.app.request.scriptUrl`
| `craft.request.getPathInfo()`                           | `craft.app.request.getPathInfo(true)`
| `craft.request.getRequestUri()`                         | `craft.app.request.url`
| `craft.request.getServerPort()`                         | `craft.app.request.serverPort`
| `craft.request.getUrlReferrer()`                        | `craft.app.request.referrer`
| `craft.request.getUserAgent()`                          | `craft.app.request.userAgent`
| `craft.request.getUserHostAddress()`                    | `craft.app.request.userIP`
| `craft.request.getUserHost()`                           | `craft.app.request.userHost`
| `craft.request.getPort()`                               | `craft.app.request.port`
| `craft.request.getCsrfToken()`                          | `craft.app.request.csrfToken`
| `craft.request.getQueryString()`                        | `craft.app.request.queryString`
| `craft.request.getQueryStringWithoutPath()`             | `craft.app.request.queryStringWithoutPath`
| `craft.request.getIpAddress()`                          | `craft.app.request.userIP`
| `craft.request.getClientOs()`                           | `craft.app.request.clientOs`
| `craft.sections.getAllSections()`                       | `craft.app.sections.allSections`
| `craft.sections.getEditableSections()`                  | `craft.app.sections.editableSections`
| `craft.sections.getTotalSections()`                     | `craft.app.sections.totalSections`
| `craft.sections.getTotalEditableSections()`             | `craft.app.sections.totalEditableSections`
| `craft.sections.getSectionById(id)`                     | `craft.app.sections.getSectionById(id)`
| `craft.sections.getSectionByHandle(handle)`             | `craft.app.sections.getSectionByHandle(handle)`
| `craft.systemSettings.[category]` *(magic getter)*      | `craft.app.systemSettings.getSettings('category')`
| `craft.userGroups.getAllGroups()`                       | `craft.app.userGroups.allGroups`
| `craft.userGroups.getGroupById(id)`                     | `craft.app.userGroups.getGroupById(id)`
| `craft.userGroups.getGroupByHandle(handle)`             | `craft.app.userGroups.getGroupByHandle(handle)`
| `craft.userPermissions.getAllPermissions()`             | `craft.app.userPermissions.allPermissions`
| `craft.userPermissions.getGroupPermissionsByUserId(id)` | `craft.app.userPermissions.getGroupPermissionsByUserId(id)`
| `craft.session.isLoggedIn()`                            | `not craft.app.user.isGuest`
| `craft.session.getUser()`                               | `currentUser`
| `craft.session.getRemainingSessionTime()`               | `craft.app.user.remainingSessionTime`
| `craft.session.getRememberedUsername()`                 | `craft.app.user.rememberedUsername`
| `craft.session.getReturnUrl()`                          | `craft.app.user.getReturnUrl()`
| `craft.session.getFlashes()`                            | `craft.app.session.getAllFlashes()`
| `craft.session.getFlash()`                              | `craft.app.session.getFlash()`
| `craft.session.hasFlash()`                              | `craft.app.session.hasFlash()`

## Date Formatting

Craft’s extended DateTime class has been removed in Craft 3. Here’s a list of things you used to be able to do in your templates, and what the Craft 3 equivalent is. (The DateTime object is represented by the `d` variable. In reality it could be `entry.postDate`, `now`, etc.)

| Old                               | New
| --------------------------------- | ----------------------------------
| `{{ d }}` *(treated as a string)* | `{{ d|date('Y-m-d') }}`
| `{{ d.atom() }}`                  | `{{ d|atom }}`
| `{{ d.cookie() }}`                | `{{ d|date('l, d-M-y H:i:s T')}}`
| `{{ d.day() }}`                   | `{{ d|date('j') }}`
| `{{ d.iso8601() }}`               | `{{ d|date('c') }}`
| `{{ d.localeDate() }}`            | `{{ d|date('short') }}`
| `{{ d.localeTime() }}`            | `{{ d|time('short') }}`
| `{{ d.month() }}`                 | `{{ d|date('n') }}`
| `{{ d.mySqlDateTime() }}`         | `{{ d|date('Y-m-d H:i:s') }}`
| `{{ d.nice() }}`                  | `{{ d|datetime('short') }}`
| `{{ d.rfc1036() }}`               | `{{ d|date('D, d M y H:i:s O') }}`
| `{{ d.rfc1123() }}`               | `{{ d|date('r') }}`
| `{{ d.rfc2822() }}`               | `{{ d|date('r') }}`
| `{{ d.rfc3339() }}`               | `{{ d|date('Y-m-d\\TH:i:sP') }}`
| `{{ d.rfc822() }}`                | `{{ d|date('D, d M y H:i:s O') }}`
| `{{ d.rfc850() }}`                | `{{ d|date('l, d-M-y H:i:s T') }}`
| `{{ d.rss() }}`                   | `{{ d|rss }}`
| `{{ d.uiTimestamp() }}`           | `{{ d|timestamp('short') }}`
| `{{ d.w3c() }}`                   | `{{ d|date('Y-m-d\\TH:i:sP') }}`
| `{{ d.w3cDate() }}`               | `{{ d|date('Y-m-d') }}`
| `{{ d.year() }}`                  | `{{ d|date('Y') }}`

## Currency Formatting

The `|currency` filter now maps to <api:craft\i18n\Formatter::asCurrency()>. It still works the same, but the `stripZeroCents` argument has been renamed to `stripZeros`, and pushed back a couple notches, so you will need to update your templates if you were setting that argument.

```twig
Old:
{{ num|currency('USD', true) }}
{{ num|currency('USD', stripZeroCents = true) }}

New:
{{ num|currency('USD', stripZeros = true) }}
```

## Element Queries

### Query Params

The following params have been removed:

| Element Type | Old Param          | New Param
| ------------ | ------------------ | -------------------------
| All of them  | `childOf`          | `relatedTo.sourceElement`
| All of them  | `childField`       | `relatedTo.field`
| All of them  | `parentOf`         | `relatedTo.targetElement`
| All of them  | `parentField`      | `relatedTo.field`
| All of them  | `depth`            | `level`
| Tag          | `name`             | `title`
| Tag          | `setId`            | `groupId`
| Tag          | `set`              | `group`
| Tag          | `orderBy:"name"`   | `orderBy:"title"`

The following params are now deprecated in Craft 3, and will be completely removed in Craft 4:

| Element Type | Old Param                | New Param
| ------------ | ------------------------ | ----------------------------
| All of them  | `order`                  | `orderBy`
| All of them  | `locale`                 | `siteId` or `site`
| All of them  | `localeEnabled`          | `enabledForSite`
| All of them  | `relatedTo.sourceLocale` | `relatedTo.sourceSite`
| Asset        | `source`                 | `volume`
| Asset        | `sourceId`               | `volumeId`
| Matrix Block | `ownerLocale`            | `ownerSite` or `ownerSiteId`

#### `limit` Param

The `limit` param is now set to `null` (no limit) by default, rather than 100.

#### Setting Params to Arrays

If you want to set a param value to an array, you now **must** type out the array brackets.

```twig
Old:
{% set query = craft.entries()
    .relatedTo('and', 1, 2, 3) %}

New:
{% set query = craft.entries()
    .relatedTo(['and', 1, 2, 3]) %}
```

#### Cloning Element Queries

In Craft 2, each time you call a parameter-setter method (e.g. `.type('article')`), the method would:

1. clone the `ElementCriteriaModel` object
2. set the parameter value on the cloned object
3. return the cloned object

That made it possible to execute variations of an element query, without affecting subsequent queries. For example:

```twig
{% set query = craft.entries.section('news') %}
{% set articleEntries = query.type('article').find() %}
{% set totalEntries = query.total() %}
```

Here `.type()` is applying the `type` parameter to a _clone_ of `query`, so it had no effect on `query.total()`, which will still return the total number of News entries, regardless of their entry types.

This behavior has changed in Craft 3, though. Now any time you call a parameter-setter method, the method will:

1. set the parameter value on the current element query
2. return the element query

Which means in the above code example, `totalEntries` will be set to the total _Article_ entries, as the `type` parameter will still be applied.

If you have any templates that count on the Craft 2 behavior, you can fix them using the [clone()](dev/functions.md#clone-object) function.

```twig
{% set query = craft.entries.section('news') %}
{% set articleEntries = clone(query).type('article').all() %}
{% set totalEntries = query.count() %}
```

### Query Methods

The following methods have been removed:

| Old                           | New
| ----------------------------- | -------------
| `findElementAtOffset(offset)` | `nth(offset)`

The following methods are now deprecated in Craft 3, and will be completely removed in Craft 4:

| Old             | New
| --------------- | --------------------------------------------------------
| `ids(criteria)` | `ids()` (setting criteria params here is now deprecated)
| `find()`        | `all()`
| `first()`       | `one()`
| `last()`        | `inReverse().one()` _(see [last()](#last))_
| `total()`       | `count()`

### Treating Queries as Arrays

Support for treating element queries as if they’re arrays has been deprecated in Craft 3, and will be completely removed in Craft 4.

When you need to loop over an element query, you should start explicitly calling `.all()`, which will execute the database query and return the array of results:

```twig
Old:
{% for entry in craft.entries.section('news') %}...{% endfor %}
{% for asset in entry.myAssetsField %}...{% endfor %}

New:
{% for entry in craft.entries.section('news').all() %}...{% endfor %}
{% for asset in entry.myAssetsField.all() %}...{% endfor %}
```

When you need to to get the total number of results from an element query, you should call the `.count()` method:

```twig
Old:
{% set total = craft.entries.section('news')|length %}

New:
{% set total = craft.entries.section('news').count() %}
```

Alternatively, if you already needed to fetch the actual query results, and you didn’t set the `offset` or `limit` params, you can use the [length](https://twig.symfony.com/doc/2.x/filters/length.html) filter to find the total size of the results array without the need for an extra database query.

```twig
{% set entries = craft.entries()
    .section('news')
    .all() %}
{% set total = entries|length %}
```

### `last()`

`last()` was deprecated in Craft 3 because it isn’t clear that it needs to run two database queries behind the scenes (the equivalent of `query.nth(query.count() - 1)`).

In most cases you can replace calls to `.last()` with `.inReverse().one()` and get the same result, without the extra database query. (`inReverse()` will reverse the sort direction of all of the `ORDER BY` columns in the generated SQL.)

```twig
{# Channel entries are ordered by `postDate DESC` by default, so this will swap
   it to `postDate ASC`, returning the oldest News entry: #}

{% set oldest = craft.entries()
    .section('news')
    .inReverse()
    .one() %}
```

There are two cases where `inReverse()` won’t work as expected, though:

- when there is no `ORDER BY` clause in the SQL, therefore nothing to reverse
- when the `orderBy` param contains a <api:yii\db\Expression> object

In those cases, you can just replace the `.last()` call with what it’s already doing internally:

```twig
{% set query = craft.entries()
    .section('news') %}
{% set total = query.count() %}
{% set last = query.nth(total - 1) %}
```

## Elements

The following element properties have been removed:

| Element Type | Old Property | New Property
| ------------ | ------------ | ------------
| Tag          | `name`       | `title`

The following element properties have been deprecated in Craft 3, and will be completely removed in Craft 4:

| Old      | New
| -------- | -------------------------------------------
| `locale` | `siteId`, `site.handle`, or `site.language`

## Models

The following model methods have been deprecated in Craft 3, and will be completely removed in Craft 4:

| Old                 | New
| ------------------- | ------------------------
| `getError('field')` | `getFirstError('field')`

## Locales

The following locale methods have been deprecated in Craft 3, and will be completely removed in Craft 4:

| Old                | New
| ------------------ | ------------------------------------
| `getId()`          | `id`
| `getName()`        | `getDisplayName(craft.app.language)`
| `getNativeName()`  | `getDisplayName()`

## Request Params

Your front-end `<form>`s and JS scripts that submit to a controller action will need to be updated with the following changes.

### `action` Params

`action` params must be rewritten in in `kebab-case` rather than `camelCase`.

```twig
Old:
<input type="hidden" name="action" value="entries/saveEntry">

New:
<input type="hidden" name="action" value="entries/save-entry">
```

The following controller actions have been removed:

| Old                          | New
| ---------------------------- | --------------------------
| `categories/create-category` | `categories/save-category`
| `users/validate`             | `users/verify-email`
| `users/save-profile`         | `users/save-user`

### `redirect` Params

`redirect` params must be hashed now.

```twig
Old:
<input type="hidden" name="redirect" value="foo/bar">

New:
<input type="hidden" name="redirect" value="{{ 'foo/bar'|hash }}">
```

The `redirectInput()` function is provided as a shortcut.

```twig
{{ redirectInput('foo/bar') }}
```

The following `redirect` param tokens are no longer supported:

| Controller Action               | Old Token     | New Token
| ------------------------------- | ------------- | ---------
| `entries/save-entry`            | `{entryId}`   | `{id}`
| `entry-revisions/save-draft`    | `{entryId}`   | `{id}`
| `entry-revisions/publish-draft` | `{entryId}`   | `{id}`
| `fields/save-field`             | `{fieldId}`   | `{id}`
| `globals/save-set`              | `{setId}`     | `{id}`
| `sections/save-section`         | `{sectionId}` | `{id}`
| `users/save-user`               | `{userId}`    | `{id}`

### CSRF Token Params

CSRF protection is enabled by default in Craft 3. If you didn’t already have it enabled (via the `enableCsrfProtection` config setting), each of your front-end `<form>`s and JS scripts that submit to a controller action will need to be updated with a new CSRF token param, named after your `csrfTokenName` config setting value (set to `'CRAFT_CSRF_TOKEN'` by default).

```twig
{% set csrfTokenName = craft.app.config.general.csrfTokenName %}
{% set csrfToken = craft.app.request.csrfToken %}
<input type="hidden" name="{{ csrfTokenName }}" value="{{ csrfToken }}">
```

The `csrfInput()` function is provided as a shortcut.

```twig
{{ csrfInput() }}
```

## Memcache

If you are using `memcache` for your <config:cacheMethod> config setting and you did not have `useMemcached` set to `true` in your `config/memcache.php` config file, you'll need to install memcached on your server.  Craft 3 will only use it because there is not a PHP 7 compatible version of memcache available.

## DbCache

If you are using `db` for your <config:cacheMethod> config setting, you'll need to manually execute some SQL before attempting the Craft 3 update.

```sql
DROP TABLE IF EXISTS craft_cache;

CREATE TABLE craft_cache (
    id char(128) NOT NULL PRIMARY KEY,
    expire int(11),
    data BLOB,
    dateCreated datetime NOT NULL,
    dateUpdated datetime NOT NULL,
    uid char(36) NOT NULL DEFAULT 0
);
```

Note that these examples use Craft 2’s default `tablePrefix` DB config setting of `craft`. If you have changed that config setting, you will want to adjust the examples accordingly.

## Plugins

See [Updating Plugins for Craft 3](extend/updating-plugins.md).