Skip to content

Latest commit

 

History

History
624 lines (483 loc) · 29 KB

upgrade.md

File metadata and controls

624 lines (483 loc) · 29 KB

Upgrading from Craft 2

Updating Craft CMS

The first step to upgrading your site to Craft 3 is updating the CMS itself.

Before you begin, make sure that:

  • your server meets Craft 3’s minimum requirements
  • your site is running at least Craft 2.6.2788
  • you’ve got a fresh database backup in case everything goes horribly wrong
  • Composer is installed (see step 1 of the installation instructions)

Once everything’s in order, follow these steps to update Craft:

  1. Create a composer.json file at the root of your Craft project (if you don’t have one already), and add the following properties:

    {
      "minimum-stability": "dev",
      "repositories": [
        {
          "type": "composer",
          "url": "https://asset-packagist.org"
        }
      ]
    }
  2. Open your terminal and go to your Craft project:

     cd /path/to/project
    
  3. Then run the following command to load Craft 3 (this will take a few minutes):

     composer require craftcms/cms:~3.0.0-beta.1
    

    {tip} If Composer complains that your system doesn’t have PHP 7 installed, but you know it’s not an issue because Craft will run with a different PHP install (e.g. through MAMP or Vagrant), use the --ignore-platform-reqs flag.

  4. Then run the following command to install the Craft plugin installer for Composer:

     composer require craftcms/plugin-installer
    
  5. Once all the files are in place, replace the contents of your public/index.php file (or public_htm/index.php, etc.) with this code: (Be sure to preserve any PHP constants and other custom code you may have.)

    <?php
    
    // Project root path
    $root = dirname(__DIR__);
    
    // Load and run Craft
    define('CRAFT_BASE_PATH', $root.'/craft');
    require_once $root.'/vendor/autoload.php';
    $app = require $root.'/vendor/craftcms/cms/bootstrap/web.php';
    $app->run();
  6. Point your browser to your Control Panel URL (e.g. http://example.dev/admin). If you see the update prompt, you did everything right! Go ahead and click “Finish up” to update your database.

  7. Delete your old craft/app/ folder. It’s no longer needed; Craft 3 is located in vendor/craftcms/cms/ now.

Configuration

Config Settings

The following config settings have been renamed or removed:

File Old Setting New Setting
general.php appId (n/a)
general.php defaultFilePermissions defaultFileMode1
general.php defaultFolderPermissions defaultDirMode
general.php useWriteFileLock useFileLocks
general.php backupDbOnUpdate backupOnUpdate2
general.php restoreDbOnUpdateFailure restoreOnUpdateFailure
general.php activateAccountFailurePath invalidUserTokenPath
db.php collation (n/a)
db.php initSQLs (n/a)

1 defaultFileMode is now null by default, meaning it will be determined by the current environment.

2 Performance should no longer be a major factor when setting backupOnUpdate to false, since backups aren’t generated by PHP anymore.

Environment Variables

The concept of “Environment Variables” has been removed in Craft 3. Previously, they provided a way to define custom token values that would get parsed in a couple places:

  • The Site URL setting in Settings → General
  • The File System Path and URL settings for “Local Folder” asset sources

You can tell if your site defined any environment variables by searching for environmentVariables in your config/general.php file. If it’s there, here’s how to handle this:

Site URL

The recommended way to set your site URL is with the siteUrl config setting in config/general.php:

return [
    'siteUrl' => 'https://foo.com/',

    // ...
]

For multi-site Craft installs, you can also set that to an array:

return [
    'siteUrl' => [
        'siteHandle1' => 'https://foo.com/',
        'siteHandle2' => 'https://bar.com/',
    ],

    // ...
]

Asset Volume Settings

Craft 3 makes it possible to completely override all asset volume settings, not just the File System Path and URL settings for “Local” volumes. See Overriding Volume Settings on the Configuration page for more info.

Redactor Configs

Your Redactor configs in config/redactor/ must now be 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
// Bad:
{
  /* interesting comment */
  buttons: ['bold', 'italic']
}

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

URL Rules

If you have any URL rules saved in config/routes.php, you will need to update them to Yii 2’s pattern-route syntax.

  • Named parameters in the pattern should be defined using the format (<paramName:regex>) rather than as a regular expression subpattern ((?P<paramName>regex)).
  • 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').
// Old:
'dashboard' => ['action' => 'dashboard/index'],
'settings/fields/new' => 'settings/fields/_edit',
'settings/fields/edit/(?P<fieldId>\d+)' => 'settings/fields/_edit',

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

PHP Constants

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

Old New
CRAFT_LOCALE CRAFT_SITE
CRAFT_SITE_URL Use the siteUrl config setting instead

Static Translation Files

Craft 3 still supports static translations, 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
pluginhandle Plugins’ translation messages

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

translations/
  en-US/
    app.php
    site.php

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
  • Rackspace Cloud Files coming soon
  • Google Cloud Storage coming soon

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”, set to the storage/userphotos/ folder, where Craft previously stored all user photos. However this folder is be 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. public_html/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 1 let you call macros defined by the same template using _self.macroName():

{% macro foo %}...{% endmacro %}

{{ _self.foo() }}

Twig 2 requires you to explicitly import them first:

Import just one macro:
{% from _self import foo %}
{{ foo() }}

Or all of them:
{% import _self as macros %}
{{ macros.foo() }}

Undefined Blocks

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

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

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

{% 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, and will be 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 %} {% do view.registerCssResource(path) %}
{% includejsresource path %} {% do view.registerJsResource(path) %}

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)

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

Old New
round(num) `num
getCsrfInput() csrfInput()
getHeadHtml() head()
getFootHtml() endBody()
getTranslations() `view.getTranslations()
craft.categoryGroups.getAllGroupIds() craft.app.categoryGroups.allGroupIds
craft.categoryGroups.getEditableGroupIds() craft.app.categories.editableGroupIds
craft.categoryGroups.getAllGroups() craft.app.categoryGroups.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.get('setting')
craft.config.get(setting) craft.app.config.get(setting)
craft.config.usePathInfo() craft.app.config.usePathInfo
craft.config.omitScriptNameInUrls() craft.app.config.omitScriptNameInUrls
craft.config.getResourceTrigger() craft.app.config.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
craft.request.getLastSegment() `craft.app.request.segments
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.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.tasks.getRunningTask() craft.app.tasks.runningTask
craft.tasks.isTaskRunning() craft.app.tasks.isTaskRunning
craft.tasks.areTasksPending() craft.app.tasks.areTasksPending()
craft.tasks.haveTasksFailed() craft.app.tasks.haveTasksFailed
craft.tasks.getTotalTasks() craft.app.tasks.totalTasks
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
{{ d.day() }} `{{ d
{{ d.month() }} `{{ d
{{ d.year() }} `{{ d
{{ d.atom() }} `{{ d
{{ d.rss() }} `{{ d
{{ d.localeDate() }} `{{ d
{{ d.localeTime() }} `{{ d
{{ d.nice() }} `{{ d
{{ d.uiTimestamp() }} `{{ d
{{ d.cookie() }} `{{ d
{{ d.iso8601() }} `{{ d
{{ d.rfc822() }} `{{ d
{{ d.rfc850() }} `{{ d
{{ d.rfc1036() }} `{{ d
{{ d.rfc1123() }} `{{ d
{{ d.rfc2822() }} `{{ d
{{ d.rfc3339() }} `{{ d
{{ d.w3c() }} `{{ d
{{ d.w3cDate() }} `{{ d
{{ d.mySqlDateTime() }} `{{ d

Element Queries

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, and will be 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

Also note that the limit param is now set to null (no limit) by default, rather than 100.

Methods

The following methods have been removed:

Old New
findElementAtOffset(offset) nth(offset)

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

Old New
ids(criteria) ids() (setting criteria params here is now deprecated)
find() all()
first() one()
last() nth(query.count() - 1)
total() count()

Elements

The following element properties have been removed:

Element Type Old Property New Property
Tag name title

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

Old New
locale siteId, site.handle, or site.language

Models

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

Old New
getError('field') getFirstError('field')

Locales

The following locale methods have been deprecated, and will be 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 snake-case rather than camelCase.

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.

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.

{{ 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).

{% set csrfTokenName = craft.app.config.get('csrfTokenName') %}
{% set csrfToken = craft.app.request.csrfToken %}
<input type="hidden" name="{{ csrfTokenName }}" value="{{ csrfToken }}">

The csrfInput() function is provided as a shortcut.

{{ csrfInput() }}

Memcache

If you are using memcache for your cacheMethod config setting and you did not have useMemcached set to true in your craft/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.

Plugins

See Updating Plugins for Craft 3.