diff --git a/docs/en/appendices/5-0-migration-guide.md b/docs/en/appendices/5-0-migration-guide.md deleted file mode 100644 index 4492d6eec4..0000000000 --- a/docs/en/appendices/5-0-migration-guide.md +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: "5.0 Migration Guide" -description: "Upgrade to CakePHP 5.0: migrate from 4.x, handle breaking changes, update code, understand new requirements and deprecation removals." ---- - -# 5.0 Migration Guide - -CakePHP 5.0 contains breaking changes, and is not backwards compatible with 4.x -releases. Before attempting to upgrade to 5.0, first upgrade to 4.5 and resolve -all deprecation warnings. - -Refer to the [5.0 Upgrade Guide](../appendices/5-0-upgrade-guide) for step by step instructions -on how to upgrade to 5.0. - -## Deprecated Features Removed - -All methods, properties and functionality that were emitting deprecation warnings -as of 4.5 have been removed. - -## Breaking Changes - -In addition to the removal of deprecated features there have been breaking -changes made: - -### Global - -- Type declarations were added to all function parameter and returns where possible. These are intended - to match the docblock annotations, but include fixes for incorrect annotations. - -- Type declarations were added to all class properties where possible. These also include some fixes for - incorrect annotations. - -- The `SECOND`, `MINUTE`, `HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR` constants were removed. - -- Use of `#[\AllowDynamicProperties]` removed everywhere. It was used for the following classes: - - `Command/Command` - - `Console/Shell` - - `Controller/Component` - - `Controller/Controller` - - `Mailer/Mailer` - - `View/Cell` - - `View/Helper` - - `View/View` - -- The supported database engine versions were updated: - - MySQL (5.7 or higher) - - MariaDB (10.1 or higher) - - PostgreSQL (9.6 or higher) - - Microsoft SQL Server (2012 or higher) - - SQLite 3 (3.16 or higher) - -### Auth - -- `Auth` has been removed. Use the [cakephp/authentication](https://book.cakephp.org/authentication/3/en/index.html) and - [cakephp/authorization](https://book.cakephp.org/authorization/3/en/index.html) plugins instead. - -### Cache - -- The `Wincache` engine was removed. The wincache extension is not supported - on PHP 8. - -### Collection - -- `combine()` now throws an exception if the key path or group path doesn't exist or contains a null value. - This matches the behavior of `indexBy()` and `groupBy()`. - -### Console - -- `BaseCommand::__construct()` was removed. -- `ConsoleIntegrationTestTrait::useCommandRunner()` was removed since it's no longer needed. -- `Shell` has been removed and should be replaced with [Command](https://book.cakephp.org/5/en/console-commands/commands.html) -- `ConsoleOptionParser::addSubcommand()` was removed alongside the removal of - `Shell`. Subcommands should be replaced with `Command` classes that - implement `Command::defaultName()` to define the necessary command name. -- `BaseCommand` now emits `Command.beforeExecute` and - `Command.afterExecute` events around the command's `execute()` method - being invoked by the framework. - -### Connection - -- `Connection::prepare()` has been removed. You can use `Connection::execute()` - instead to execute a SQL query by specifing the SQL string, params and types in a single call. -- `Connection::enableQueryLogging()` has been removed. If you haven't enabled logging - through the connection config then you can later set the logger instance for the - driver to enable query logging `$connection->getDriver()->setLogger()`. - -### Controller - -- The method signature for `Controller::__construct()` has changed. - So you need to adjust your code accordingly if you are overriding the constructor. -- After loading components are no longer set as dynamic properties. Instead - `Controller` uses `__get()` to provide property access to components. This - change can impact applications that use `property_exists()` on components. -- The components' `Controller.shutdown` event callback has been renamed from - `shutdown` to `afterFilter` to match the controller one. This makes the callbacks more consistent. -- `PaginatorComponent` has been removed and should be replaced by calling `$this->paginate()` in your controller or - using `Cake\Datasource\Paging\NumericPaginator` directly -- `RequestHandlerComponent` has been removed. See the [4.4 migration](https://book.cakephp.org/4/en/appendices/4-4-migration-guide.html#requesthandlercomponent) guide for how to upgrade -- `SecurityComponent` has been removed. Use `FormProtectionComponent` for form tampering protection - or `HttpsEnforcerMiddleware` to enforce use of HTTPS for requests instead. -- `Controller::paginate()` no longer accepts query options like `contain` for - its `$settings` argument. You should instead use the `finder` option - `$this->paginate($this->Articles, ['finder' => 'published'])`. Or you can - create required select query before hand and then pass it to `paginate()` - `$query = $this->Articles->find()->where(['is_published' => true]); $this->paginate($query);`. - -### Core - -- The function `getTypeName()` has been dropped. Use PHP's `get_debug_type()` instead. -- The dependency on `league/container` was updated to `4.x`. This will - require the addition of typehints to your `ServiceProvider` implementations. -- `deprecationWarning()` now has a `$version` parameter. -- The `App.uploadedFilesAsObjects` configuration option has been removed - alongside of support for PHP file upload shaped arrays throughout the - framework. -- `ClassLoader` has been removed. Use composer to generate autoload files instead. - -### Database - -- The `DateTimeType` and `DateType` now always return immutable objects. - Additionally, the interface for `Date` objects reflects the `ChronosDate` - interface which lacks all the time related methods that were present in - CakePHP 4.x. -- `DateType::setLocaleFormat()` no longer accepts an array. -- `Query` now accepts only `\Closure` parameters instead of `callable`. Callables can be converted - to closures using the new first-class array syntax in PHP 8.1. -- `Query::execute()` no longer runs results decorator callbacks. You must use `Query::all()` instead. -- `TableSchemaAwareInterface` was removed. -- `Driver::quote()` was removed. Use prepared statements instead. -- `Query::orderBy()` was added to replace `Query::order()`. -- `Query::groupBy()` was added to replace `Query::group()`. -- `SqlDialectTrait` has been removed and all its functionality has been moved - into the `Driver` class itself. -- `CaseExpression` has been removed and should be replaced with - `QueryExpression::case()` or `CaseStatementExpression` -- `Connection::connect()` has been removed. Use - `$connection->getDriver()->connect()` instead. -- `Connection::disconnect()` has been removed. Use - `$connection->getDriver()->disconnect()` instead. -- `cake.database.queries` has been added as an alternative to the `queriesLog` scope -- The ability to enable/disable ResultSet buffering has been removed. Results are always buffered. - -### Datasource - -- The `getAccessible()` method was added to `EntityInterface`. Non-ORM - implementations need to implement this method now. -- The `aliasField()` method was added to `RepositoryInterface`. Non-ORM - implementations need to implement this method now. - -### Event - -- Event payloads must be an array. Other object such as `ArrayAccess` are no longer cast to array and will raise a `TypeError` now. -- It is recommended to adjust event handlers to be void methods and use `$event->setResult()` instead of returning the result - -### Error - -- `ErrorHandler` and `ConsoleErrorHandler` have been removed. See the [4.4 migration](https://book.cakephp.org/4/en/appendices/4-4-migration-guide.html#errorhandler-consoleerrorhandler) guide for how to upgrade -- `ExceptionRenderer` has been removed and should be replaced with `WebExceptionRenderer` -- `ErrorLoggerInterface::log()` has been removed and should be replaced with `ErrorLoggerInterface::logException()` -- `ErrorLoggerInterface::logMessage()` has been removed and should be replaced with `ErrorLoggerInterface::logError()` - -### Filesystem - -- The Filesystem package was removed, and `Filesystem` class was moved to the Utility package. - -### Http - -- `ServerRequest` is no longer compatible with `files` as arrays. This - behavior has been disabled by default since 4.1.0. The `files` data will now - always contain `UploadedFileInterfaces` objects. - -### I18n - -- `FrozenDate` was renamed to `Date` and `FrozenTime` was renamed to `DateTime`. -- `Time` now extends `Cake\Chronos\ChronosTime` and is therefore immutable. -- `Date` objects do not extend `DateTimeInterface` anymore - therefore you can't compare them with `DateTime` objects. - See the [cakephp/chronos release documentation](https://github.com/cakephp/chronos/releases/tag/3.0.2) for more information. -- `Date::parseDateTime()` was removed. -- `Date::parseTime()` was removed. -- `Date::setToStringFormat()` and `Date::setJsonEncodeFormat()` no longer accept an array. -- `Date::i18nFormat()` and `Date::nice()` no longer accept a timezone parameter. -- Translation files for plugins with vendor prefixed names (`FooBar/Awesome`) will now have that - prefix in the file name, e.g. `foo_bar_awesome.po` to avoid collision with a `awesome.po` file - from a corresponding plugin (`Awesome`). - -### Log - -- Log engine config now uses `null` instead of `false` to disable scopes. - So instead of `'scopes' => false` you need to use `'scopes' => null` in your log config. - -### Mailer - -- `Email` has been removed. Use [Mailer](https://book.cakephp.org/5/en/core-libraries/email.html) instead. -- `cake.mailer` has been added as an alternative to the `email` scope - -### ORM - -- `EntityTrait::has()` now returns `true` when an attribute exists and is - set to `null`. In previous versions of CakePHP this would return `false`. - See the release notes for 4.5.0 for how to adopt this behavior in 4.x. -- `EntityTrait::extractOriginal()` now returns only existing fields, similar to `extractOriginalChanged()`. -- Finder arguments are now required to be associative arrays as they were always expected to be. -- `TranslateBehavior` now defaults to the `ShadowTable` strategy. If you are - using the `Eav` strategy you will need to update your behavior configuration - to retain the previous behavior. -- `allowMultipleNulls` option for `isUnique` rule now default to true matching - the original 3.x behavior. -- `Table::query()` has been removed in favor of query-type specific functions. -- `Table::updateQuery()`, `Table::selectQuery()`, `Table::insertQuery()`, and - `Table::deleteQuery()`) were added and return the new type-specific query objects below. -- `SelectQuery`, `InsertQuery`, `UpdateQuery` and `DeleteQuery` were added - which represent only a single type of query and do not allow switching between query types nor - calling functions unrelated to the specific query type. -- `Table::_initializeSchema()` has been removed and should be replaced by calling - `$this->getSchema()` inside the `initialize()` method. -- `SaveOptionsBuilder` has been removed. Use a normal array for options instead. - -### Known Issues - -**Memory usage with large result sets** - -Compared to CakePHP 4.x, when working with large data sets (especially when -calling collection methods like `extract()` on the result set), you may encounter -high memory usage due to the entire result set being buffered in memory. - -You can work around this issue by disabling results buffering for the query: - -```php -$results = $articles->find() - ->disableBufferedResults() - ->all(); -``` - -Depending on your use case, you may also consider using disabling hydration: - -```php -$results = $articles->find() - ->disableHydration() - ->all(); -``` - -The above will disable creation of entity objects and return rows as arrays instead. - -### Routing - -- Static methods `connect()`, `prefix()`, `scope()` and `plugin()` of the `Router` have been removed and - should be replaced by calling their non-static method variants via the `RouteBuilder` instance. -- `RedirectException` has been removed. Use `\Cake\Http\Exception\RedirectException` instead. - -### TestSuite - -- `TestSuite` was removed. Users should use environment variables to customize - unit test settings instead. -- `TestListenerTrait` was removed. PHPUnit dropped support for these listeners. - See [PHPUnit Upgrade](../appendices/phpunit-upgrade) -- `IntegrationTestTrait::configRequest()` now merges config when called multiple times - instead of replacing the currently present config. - -### Validation - -- `Validation::isEmpty()` is no longer compatible with file upload shaped - arrays. Support for PHP file upload arrays has been removed from - `ServerRequest` as well so you should not see this as a problem outside of - tests. -- Previously, most data validation error messages were simply `The provided value is invalid`. - Now, the data validation error messages are worded more precisely. - For example, ``The provided value must be greater than or equal to `5`.`` - -### View - -- `ViewBuilder` options are now truly associative (string keys). -- `NumberHelper` and `TextHelper` no longer accept an `engine` config. -- `ViewBuilder::setHelpers()` parameter `$merge` was removed. Use `ViewBuilder::addHelpers()` instead. -- Inside `View::initialize()`, prefer using `addHelper()` instead of `loadHelper()`. - All configured helpers will be loaded afterwards, anyway. -- `View\Widget\FileWidget` is no longer compatible with PHP file upload shaped - arrays. This is aligned with `ServerRequest` and `Validation` changes. -- `FormHelper` no longer sets `autocomplete=off` on CSRF token fields. This - was a workaround for a Safari bug that is no longer relevant. - -## Deprecations - -The following is a list of deprecated methods, properties and behaviors. These -features will continue to function in 5.x and will be removed in 6.0. - -### Database - -- `Query::order()` was deprecated. Use `Query::orderBy()` instead now that - `Connection` methods are no longer proxied. This aligns the function name - with the SQL statement. -- `Query::group()` was deprecated. Use `Query::groupBy()` instead now that - `Connection` methods are no longer proxied. This aligns the function name - with the SQL statement. - -### ORM - -- Calling `Table::find()` with options array is deprecated. Use [named arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments) - instead. For e.g. instead of `find('all', ['conditions' => $array])` use - `find('all', conditions: $array)`. Similarly for custom finder options, instead - of `find('list', ['valueField' => 'name'])` use `find('list', valueField: 'name')` - or multiple named arguments like `find(type: 'list', valueField: 'name', conditions: $array)`. - -## New Features - -### Improved type checking - -CakePHP 5 leverages the expanded type system feature available in PHP 8.1+. -CakePHP also uses `assert()` to provide improved error messages and additional -type soundness. In production mode, you can configure PHP to not generate -code for `assert()` yielding improved application performance. See the -[Symlink Assets](../deployment#symlink-assets) for how to do this. - -### Collection - -- Added `unique()` which filters out duplicate value specified by provided callback. -- `reject()` now supports a default callback which filters out truthy values which is - the inverse of the default behavior of `filter()` - -### Core - -- The `services()` method was added to `PluginInterface`. -- `PluginCollection::addFromConfig()` has been added to [simplify plugin loading](../plugins#loading-a-plugin). - -### Database - -- `ConnectionManager` now supports read and write connection roles. Roles can be configured - with `read` and `write` keys in the connection config that override the shared config. -- `Query::all()` was added which runs result decorator callbacks and returns a result set for select queries. -- `Query::comment()` was added to add a SQL comment to the executed query. This makes it easier to debug queries. -- `EnumType` was added to allow mapping between PHP backed enums and a string or integer column. -- `getMaxAliasLength()` and `getConnectionRetries()` were added - to `DriverInterface`. -- Supported drivers now automatically add auto-increment only to integer primary keys named "id" instead - of all integer primary keys. Setting 'autoIncrement' to false always disables on all supported drivers. - -### Http - -- Added support for [PSR-17](https://www.php-fig.org/psr/psr-17/) factories - interface. This allows `cakephp/http` to provide a client implementation to - libraries that allow automatic interface resolution like php-http. -- Added `CookieCollection::__get()` and `CookieCollection::__isset()` to add - ergonomic ways to access cookies without exceptions. - -### ORM - -### Required Entity Fields - -Entities have a new opt-in functionality that allows making entities handle -properties more strictly. The new behavior is called 'required fields'. When -enabled, accessing properties that are not defined in the entity will raise -exceptions. This impacts the following usage: - -```php -$entity->get(); -$entity->has(); -$entity->getOriginal(); -isset($entity->attribute); -$entity->attribute; -``` - -Fields are considered defined if they pass `array_key_exists`. This includes -null values. Because this can be a tedious to enable feature, it was deferred to -5.0. We'd like any feedback you have on this feature as we're considering making -this the default behavior in the future. - -### Typed Finder Parameters - -Table finders can now have typed arguments as required instead of an options array. -For e.g. a finder for fetching posts by category or user: - -```php -public function findByCategoryOrUser(SelectQuery $query, array $options): SelectQuery -{ - if (isset($options['categoryId'])) { - $query->where(['category_id' => $options['categoryId']]); - } - if (isset($options['userId'])) { - $query->where(['user_id' => $options['userId']]); - } - - return $query; -} -``` - -can now be written as: - -```php -public function findByCategoryOrUser(SelectQuery $query, ?int $categoryId = null, ?int $userId = null): SelectQuery -{ - if ($categoryId) { - $query->where(['category_id' => $categoryId]); - } - if ($userId) { - $query->where(['user_id' => $userId]); - } - - return $query; -} -``` - -The finder can then be called as `find('byCategoryOrUser', userId: $somevar)`. -You can even include the special named arguments for setting query clauses. -`find('byCategoryOrUser', userId: $somevar, conditions: ['enabled' => true])`. - -A similar change has been applied to the `RepositoryInterface::get()` method: - -```php -public function view(int $id) -{ - $author = $this->Authors->get($id, [ - 'contain' => ['Books'], - 'finder' => 'latest', - ]); -} -``` - -can now be written as: - -```php -public function view(int $id) -{ - $author = $this->Authors->get($id, contain: ['Books'], finder: 'latest'); -} -``` - -### TestSuite - -- `IntegrationTestTrait::requestAsJson()` has been added to set JSON headers for the next request. - -### Plugin Installer - -- The plugin installer has been updated to automatically handle class autoloading - for your app plugins. So you can remove the namespace to path mappings for your - plugins from your `composer.json` and just run `composer dumpautoload`. diff --git a/docs/en/appendices/5-0-upgrade-guide.md b/docs/en/appendices/5-0-upgrade-guide.md deleted file mode 100644 index 7c458f713b..0000000000 --- a/docs/en/appendices/5-0-upgrade-guide.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: "5.0 Upgrade Guide" -description: "Upgrade to CakePHP 5.0 from 4.x. Fix deprecations, update to PHP 8.1+, run rector tool, and update dependencies following this step-by-step guide." ---- - -# 5.0 Upgrade Guide - -First, check that your application is running on latest CakePHP 4.x version. - -## Fix Deprecation Warnings - -Once your application is running on latest CakePHP 4.x, enable deprecation warnings in **config/app.php**: - -```php -'Error' => [ - 'errorLevel' => E_ALL, -], -``` - -Now that you can see all the warnings, make sure these are fixed before proceeding with the upgrade. - -Some potentially impactful deprecations you should make sure you have addressed -are: - -- `Table::query()` was deprecated in 4.5.0. Use `selectQuery()`, - `updateQuery()`, `insertQuery()` and `deleteQuery()` instead. - -## Upgrade to PHP 8.1 - -If you are not running on **PHP 8.1 or higher**, you will need to upgrade PHP before updating CakePHP. - -> [!NOTE] -> CakePHP 5.0 requires **a minimum of PHP 8.1**. - - - -## Use the Upgrade Tool - -> [!NOTE] -> The upgrade tool only works on applications running on latest CakePHP 4.x. You cannot run the upgrade tool after updating to CakePHP 5.0. - -Because CakePHP 5 leverages union types and `mixed`, there are many -backwards incompatible changes concerning method signatures and file renames. -To help expedite fixing these tedious changes there is an upgrade CLI tool: - -```bash -# Install the upgrade tool -git clone https://github.com/cakephp/upgrade -cd upgrade -git checkout 5.x -composer install --no-dev -``` - -With the upgrade tool installed you can now run it on your application or -plugin: - -```bash -bin/cake upgrade rector --rules cakephp50 -bin/cake upgrade rector --rules chronos3 -``` - -## Update CakePHP Dependency - -After applying rector refactorings you need to upgrade CakePHP, its plugins, PHPUnit -and maybe other dependencies in your `composer.json`. -This process heavily depends on your application so we recommend you compare your -`composer.json` with what is present in [cakephp/app](https://github.com/cakephp/app/blob/5.x/composer.json). - -After the version strings are adjusted in your `composer.json` execute -`composer update -W` and check its output. - -## Update app files based upon latest app template - -Next, ensure the rest of your application has been updated to be based upon the -latest version of [cakephp/app](https://github.com/cakephp/app/blob/5.x/). diff --git a/docs/en/appendices/5-1-migration-guide.md b/docs/en/appendices/5-1-migration-guide.md deleted file mode 100644 index dadebc3e70..0000000000 --- a/docs/en/appendices/5-1-migration-guide.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: "5.1 Migration Guide" -description: "Migrate to CakePHP 5.1: explore new features, deprecations, improvements, and upgrade your 5.0 application with this comprehensive guide." ---- - -# 5.1 Migration Guide - -The 5.1.0 release is backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. - -## Upgrade Tool - -The [upgrade tool](../appendices/migration-guides) provides rector rules for -automating some of the migration work. Run rector before updating your -`composer.json` dependencies: - -```bash -bin/cake upgrade rector --rules cakephp51 -``` - -## Behavior Changes - -- Connection now creates unique read and write drivers if the keys `read` or - `write` are present in the config regardless of values. -- FormHelper no longer generates `aria-required` attributes on input elements - that also have the `required` attribute set. The `aria-required` attribute - is redundant on these elements and generates HTML validation warnings. If you - are using `aria-required` attribute in styling or scripting you'll need to - update your application. -- Adding associations with duplicate names will now raise exceptions. You can - use `$table->associations()->has()` to conditionally define associations if - required. -- Text Utility and TextHelper methods around truncation and maximum length are using - a UTF-8 character for `ellipsis` instead of `...` legacy characters. -- `TableSchema::setColumnType()` now throws an exception if the specified column - does not exist. -- `PluginCollection::addPlugin()` now throws an exception if a plugin of the same - name is already added. -- `TestCase::loadPlugins()` will now clear out any previously loaded plugins. So - you must specify all plugins required for any subsequent tests. -- The hashing algorithm for `Cache` configurations that use `groups`. Any - keys will have new group prefix hashes generated which will cause cache - misses. Consider an incremental deploy to avoid operating on an entirely cold - cache. -- `FormHelper::getFormProtector()` now returns `null` in addition to its - previous types. This allows dynamic view code to run with fewer errors and - shouldn't impact most applications. -- The default value for `valueSeparator` in `Table::findList()` is now - a single space instead of `;`. -- `ErrorLogger` uses `Psr\Log\LogTrait` now. -- `Database\QueryCompiler::$_orderedUnion` was removed. - -## Deprecations - -### I18n - -- The `_cake_core_` cache config key has been renamed to `_cake_translations_`. - -### Mailer - -- `Mailer::setMessage()` is deprecated. It has unintuitive behavior and very - low usage. - -## New Features - -### Cache - -- `RedisEngine` now supports a `tls` option that enables connecting to redis - over a TLS connection. You can use the `ssl_ca`, `ssl_cert` and - `ssl_key` options to define the TLS context for redis. - -### Command - -- `bin/cake plugin list` has been added to list all available plugins, - their load configuration and version. -- Optional `Command` arguments can now have a `default` value. -- `BannerHelper` was added. This command helper can format text as a banner - with a coloured background and padding. -- Additional default styles for `info.bg`, `warning.bg`, `error.bg` and - `success.bg` were added to `ConsoleOutput`. - -### Console - -- `Arguments::getBooleanOption()` and `Arguments::getMultipleOption()` were added. -- `Arguments::getArgument()` will now raise an exception if an unknown - argument name is provided. This helps prevent mixing up option/argument names. - -### Controller - -- Components can now use the DI container to have dependencies resolved and - provided as constructor parameters just like Controllers and Commands do. - -### Core - -- `PluginConfig` was added. Use this class to get all available plugins, their load config and versions. -- The `toString`, `toInt`, `toBool` functions were added. They give you - a typesafe way to cast request data or other input and return `null` when conversion fails. -- `pathCombine()` was added to help build paths without worrying about duplicate and trailing slashes. -- A new `events` hook was added to the `BaseApplication` as well as the `BasePlugin` class. This hook - is the recommended way to register global event listeners for you application. See [Registering Listeners](../core-libraries/events#registering-event-listeners) - -### Database - -- Support for `point`, `linestring`, `polygon` and `geometry` types were - added. These types are useful when working with geospatial or cartesian - co-ordinates. Sqlite support uses text columns under the hood and lacks - functions to manipulate data as geospatial values. -- `SelectQuery::__debugInfo()` now includes which connection role the query - is for. -- `SelectQuery::intersect()` and `SelectQuery::intersectAll()` were added. - These methods enable queries using `INTERSECT` and `INTERSECT ALL` - conjunctions to be expressed. -- New supports features were added for `intersect`, `intersect-all` and - `set-operations-order-by` features. -- The ability to fetch records without buffering which existed in 4.x has been restored. - Methods `SelectQuery::enableBufferedResults()`, `SelectQuery::disableBufferedResults()` - and `SelectQuery::isBufferedResultsEnabled()` have been re-added. - -### Datasource - -- `RulesChecker::remove()`, `removeCreate()`, `removeUpdate()`, and - `removeDelete()` methods were added. These methods allow you to remove rules - by name. - -### Http - -- `SecurityHeadersMiddleware::setPermissionsPolicy()` was added. This method - adds the ability to define `permissions-policy` header values. -- `Client` now emits `HttpClient.beforeSend` and `HttpClient.afterSend` - events when requests are sent. You can use these events to perform logging, - caching or collect telemetry. -- `Http\Server::terminate()` was added. This method triggers the - `Server.terminate` event which can be used to run logic after the response - has been sent in fastcgi environments. In other environments the - `Server.terminate` event runs *before* the response has been sent. - -### I18n - -- `Number::formatter()` and `currency()` now accept a `roundingMode` - option to override how rounding is done. -- The `toDate`, and `toDateTime` functions were added. They give you - a typesafe way to cast request data or other input and return `null` when - conversion fails. - -### ORM - -- Setting the `preserveKeys` option on association finder queries. This can be - used with `formatResults()` to replace association finder results with an - associative array. -- SQLite columns with names containing `json` can now be mapped to `JsonType`. - This is currently an opt-in feature which is enabled by setting the `ORM.mapJsonTypeForSqlite` - configure value to `true` in your app. - -### TestSuite - -- CakePHP as well as the app template have been updated to use PHPUnit `^10.5.5 || ^11.1.3"`. - `ConnectionHelper` methods are now all static. This class has no state and its methods were updated to be static. - `LogTestTrait` was added. This new trait makes it easy to capture logs in your tests and make assertions on the presence or absence of log messages. - `IntegrationTestTrait::replaceRequest()` was added. - -### Utility - -- `Hash::insert()` and `Hash::remove()` now accept `ArrayAccess` objects along with `array` data. - -### Validation - -- `Validation::enum()` and `Validator::enum()` were added. These validation - methods simplify validating backed enum values. -- `Validation::enumOnly()` and `Validation::enumExcept()` were added to check for specific cases - and further simplify validating backed enum values. - -### View - -- View cells now emit events around their actions `Cell.beforeAction` and - `Cell.afterAction`. -- `NumberHelper::format()` now accepts a `roundingMode` option to override how - rounding is done. - -### Helpers - -- `TextHelper::autoLinkUrls()` has options added for better link label printing: - - `stripProtocol`: Strips `http://` and `https://` from the beginning of the link. Default off. - - `maxLength`: The maximum length of the link label. Default off. - - `ellipsis`: The string to append to the end of the link label. Defaults to UTF8 version. -- `HtmlHelper::meta()` can now create a meta tag containing the current CSRF - token using `meta('csrfToken')`. diff --git a/docs/en/appendices/5-2-migration-guide.md b/docs/en/appendices/5-2-migration-guide.md deleted file mode 100644 index 277ac03aa5..0000000000 --- a/docs/en/appendices/5-2-migration-guide.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: "5.2 Migration Guide" -description: "Migrate to CakePHP 5.2: understand new features, deprecations, breaking changes, and upgrade your application from 5.0/5.1 smoothly." ---- - -# 5.2 Migration Guide - -The 5.2.0 release is backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. - -## Upgrade Tool - -The [upgrade tool](../appendices/migration-guides) provides rector rules for -automating some of the migration work. Run rector before updating your -`composer.json` dependencies: - -```bash -bin/cake upgrade rector --rules cakephp52 -``` - -## Behavior Changes - -- `ValidationSet::add()` will now raise errors when a rule is added with - a name that is already defined. This change aims to prevent rules from being - overwritten by accident. -- `Http\Session` will now raise an exception when an invalid session preset is - used. -- `FormProtectionComponent` now raises `Cake\Controller\Exception\FormProtectionException`. This - class is a subclass of `BadRequestException`, and offers the benefit of - being filterable from logging. -- `NumericPaginator::paginate()` now uses the `finder` option even when a `SelectQuery` instance is passed to it. - -## Deprecations - -### Console - -- `Arguments::getMultipleOption()` is deprecated. Use `getArrayOption()` - instead. - -### Datasource - -- The ability to cast an `EntityInterface` instance to string has been deprecated. - You should `json_encode()` the entity instead. -- Mass assigning multiple entity fields using `EntityInterface::set()` is deprecated. - Use `EntityInterface::patch()` instead. For e.g. change usage like - `$entity->set(['field1' => 'value1', 'field2' => 'value2'])` to - `$entity->patch(['field1' => 'value1', 'field2' => 'value2'])`. - -### Event - -- Returning values from event listeners / callbacks is deprecated. Use `$event->setResult()` - instead or `$event->stopPropagation()` to just stop the event propagation. - -### View - -- The `errorClass` option of `FormHelper` has been deprecated in favour of - using a template string. To upgrade move your `errorClass` definition to - a template set. See [Customizing Templates](../views/helpers/form#customizing-templates). - -## New Features - -### Console - -- The `cake counter_cache` command was added. This command can be used to - regenerate counters for models that use `CounterCacheBehavior`. -- `ConsoleIntegrationTestTrait::debugOutput()` makes it easier to debug - integration tests for console commands. -- `ConsoleInputArgument` now supports a `separator` option. This option - allows positional arguments to be delimited with a character sequence like - `,`. CakePHP will split the positional argument into an array when arguments - are parsed. -- `Arguments::getArrayArgumentAt()`, and `Arguments::getArrayArgument()` - were added. These methods allow you to read `separator` delimitered - positional arguments as arrays. -- `ConsoleInputOption` now supports a `separator` option. This option - allows option values to be delimited with a character sequence like - `,`. CakePHP will split the option value into an array when arguments - are parsed. -- `Arguments::getArrayArgumentAt()`, `Arguments::getArrayArgument()`, and - `Arguments::getArrayOption()` - were added. These methods allow you to read `separator` delimitered - positional arguments as arrays. - -### Database - -- The `nativeuuid` type was added. This type enables `uuid` columns to be - used in Mysql connections with MariaDB. In all other drivers, `nativeuuid` - is an alias for `uuid`. -- `Cake\Database\Type\JsonType::setDecodingOptions()` was added. This method - lets you define the value for the `$flags` argument of `json_decode()`. -- `CounterCacheBehavior::updateCounterCache()` was added. This method allows - you to update the counter cache values for all records of the configured - associations. `CounterCacheCommand` was also added to do the same through the - console. -- `Cake\Database\Driver::quote()` was added. This method provides a way to - quote values to be used in SQL queries where prepared statements cannot be - used. - -### Datasource - -- Application rules can now use `Closure` to define the validation message. - This allows you to create dynamic validation messages based on the entity - state and validation rule options. - -### Error - -- Custom exceptions can have specific error handling logic defined in - `ErrorController`. - -### ORM - -- `CounterCacheBehavior::updateCounterCache()` has been added. This method - allows you to update the counter cache values for all records of the configured - associations. -- `BelongsToMany::setJunctionProperty()` and `getJunctionProperty()` were - added. These methods allow you to customize the `_joinData` property that is - used to hydrate junction table records. -- `Table::findOrCreate()` now accepts an array as second argument to directly pass data in. - -### TestSuite - -- `TestFixture::$strictFields` was added. Enabling this property will make - fixtures raise an error if a fixture's record list contains fields that do not - exist in the schema. - -### View - -- `FormHelper::deleteLink()` has been added as convenience wrapper for delete links in - templates using `DELETE` method. -- `HtmlHelper::importmap()` was added. This method allows you to define - import maps for your JavaScript files. -- `FormHelper` now uses the `containerClass` template to apply a class to - the form control div. The default value is `input`. diff --git a/docs/en/appendices/5-3-migration-guide.md b/docs/en/appendices/5-3-migration-guide.md deleted file mode 100644 index ba29464945..0000000000 --- a/docs/en/appendices/5-3-migration-guide.md +++ /dev/null @@ -1,266 +0,0 @@ ---- -title: "5.3 Migration Guide" -description: "Upgrade to CakePHP 5.3: discover new features, deprecations, improvements, and migrate from 5.0-5.2 with this detailed guide." ---- - -# 5.3 Migration Guide - -The 5.3.0 release is backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. - -## Upgrade Tool - -The [upgrade tool](../appendices/migration-guides) provides rector rules for -automating some of the migration work. Run rector before updating your -`composer.json` dependencies: - -```bash -bin/cake upgrade rector --rules cakephp53 -``` - -## Upgrade to PHP 8.2 - -If you are not running on **PHP 8.2 or higher**, you will need to upgrade PHP before updating CakePHP. - -> [!NOTE] -> CakePHP 5.3 requires **a minimum of PHP 8.2**. - -## Behavior Changes - -### Core - -- `InstanceConfigTrait::deleteConfig()` was added. For classes using this trait, - you can now use `$this->deleteConfig('key')` instead of `$this->setConfig('key', null)` - -### Database - -- `Query::with()` now accepts an array of expressions to align with other query clauses. - This also allows clearing the expressions with an empty array. - -### ORM - -- `joinWith()` now asserts when the association conflicts with an existing join and will overwrite it. - Because existing code might harmlessly ignore the join or accidentally rely on that behavior, this change is not breaking - in production for CakePHP 5. - -### Validation - -- The signature of `Validator::validate(array $data, bool $newRecord = true, array $context = [])` now has an additional third parameter `$context`. - It can be used to pass necessary context into the validation when marshalling. - -### View - -- The `format()` and `currency()` methods of `NumberHelper` now accept also null as input and can return any default string here. - This allows for easier templates, in particular baked ones. Make sure to adjust any extending helper (plugin or app level) by adding that type. - -## Deprecations - -### Database - -- `Query::newExpr()` is deprecated. Use `Query::expr()` instead. - -### Form - -- `Form::_execute()` is deprecated. You should rename your `_execute` - methods to `process()` which accepts the same parameters and has the same - return type. - -### Http - -- Using `$request->getParam('?')` to get the query params is deprecated. - Use `$request->getQueryParams()` instead. - -### ORM - -- Calling behavior methods on table instances is now deprecated. To call - a method of an attached behavior you need to use - `$table->getBehavior('Sluggable')->slugify()` instead of `$table->slugify()`. -- `EntityTrait::isEmpty()` is deprecated. Use `hasValue()` instead. - -### Plugin - -- Loading of plugins without a plugin class is deprecated. For your existing plugins - which don't have one, you can use the `bin/cake bake plugin MyPlugin --class-only` - command, which will create the file `plugins/MyPlugin/src/MyPlugin.php`. - -### View - -- Passing an array as the first argument to `BreadcrumbsHelper::add()` and - `BreadcrumbsHelper::prepend()` is deprecated. Use `addMany()` and - `prependMany()` instead. - -## New Features - -### Cache - -- Added Redis Cluster support to `RedisEngine`. Configure the `cluster` option - with an array of server addresses to enable cluster mode. -- Several [Cache Events](../core-libraries/caching#cache-events) were added to allow monitoring the caching behavior. - -### Collection - -- `Collection::any()` was added to replace `Collection::some()` with a more familiar name. - -### Command - -- `cake plugin assets symlink` command now supports a `--relative` option to - create relative path symlinks. This is useful when creating symlinks within - containers that use volume mounts. -- `cake server` now supports a `--frankenphp` option that will start the - development server with [FrankenPHP](https://frankenphp.dev/). - -### Console - -- Added `TreeHelper` which outputs an array as a tree such as an array of filesystem - directories as array keys and files as lists under each directory. -- Commands can now implement `getGroup()` to customize how commands are - grouped in `bin/cake -h` output. -- `CommandCollection::replace()` was added. This method allows you to replace - an existing command in the collection without needing to remove and re-add it. - This is particularly useful when using `autoDiscover` and you want to replace - a command with a customized version. - -### Core - -- Added `Configure` attribute to support injecting `Configure` values into - constructor arguments. See [Configure Dependency Injection](../development/dependency-injection#configure-dependency-injection). - -### Database - -- Added support for Entra authentication to SqlServer driver. -- Added `Query::optimizerHint()` which accepts engine-specific optimizer hints. -- Added `Query::getDriver()` helper which returns the `Driver` for the current connection - role by default. -- Added support for `year` column types in MySQL. -- Added support for `inet`, `cidr` and `macaddr` network column types to - postgres driver. -- Added `TypeFactory::getMapped()` to retrieve the mapped class name for a specific type. - This provides a cleaner API compared to using `TypeFactory::getMap()` with a type argument. - -### Error - -- `Debugger` now replaces `ROOT` with the - `Debugger.editorBasePath` Configure value if defined. This improves - debugging workflows within containerized environments. - -### Http - -- The new `RateLimitMiddleware` provides configurable rate limiting for your - application to protect against abuse and ensure fair usage of resources. It - supports multiple identification strategies (IP, user, route, API key), - different rate limiting algorithms (sliding window, fixed window, token bucket), - and advanced features like custom identifiers, request costs, and dynamic limits. -- `UnprocessableContentException` was added. - -### I18n - -- Added `DateTimePeriod` which wraps a php `DatePeriod` and returns `DateTime` - instances when iterating. -- Added `DatePeriod` which wraps a php `DatePeriod` and returns `Date` instances - when iterating. -- Added `toQuarterRange()` method to `DateTime` and `FrozenTime` classes which returns - an array containing the start and end dates of the quarter for the given date. -- Added `Date::getTimestamp()`. This method returns an int of the date's - timestamp. - -### Mailer - -- Added `Message::addAttachment()` for adding attachments to a message. Like - other message methods, it can be accessed via the `Mailer` instance as `$mailer->addAttachment()`. - -### ORM - -- `Table::patchEntity()`, `Table::newEntity()`, `Marshaller::one()` and - `Marshaller::many()` now accept a `strictFields` option that only applies - validation to the fields listed in the `fields` option. -- Added `TableContainer` that you can register in your `Application::services()` to - add dependency injection for your Tables. -- Added `SelectQuery::projectAs()` for projecting query results into Data - Transfer Objects (DTOs) instead of Entity objects. DTOs provide a - memory-efficient alternative (approximately 3x less memory than entities) for - read-only data access. See [Dto Projection](../orm/query-builder#dto-projection). -- Added the `#[CollectionOf]` attribute for declaring the element type of - array properties in DTOs. This enables proper hydration of nested - associations into DTOs. - -### Pagination - -- Added `SortableFieldsBuilder` class enabling fluent configuration of - sortable fields with advanced features. The `sortableFields` option now - accepts a callable that receives a `SortableFieldsBuilder` instance, - allowing you to map friendly sort keys to database fields with multi-column - sorting and direction control. -- Added `SortField` class for defining sort field configurations with - customizable default directions and locked directions (e.g., - `SortField::asc('price')` or `SortField::desc('created', locked: true)`). -- Added support for combined sorting keys in URLs (e.g., `?sort=title-asc` or - `?sort=price-desc`) in addition to the traditional `?sort=field&direction=asc` - format. - -### Routing - -- Added `RouteBuilder::setOptions()` method to set default route options at - the scope level. This allows you to apply options like `_host`, `_https`, - and `_port` to all routes within a scope without repeating them on each - route. Options set at the scope level are inherited by nested scopes and can - be overridden on individual routes. -- `EntityRoute` now handles enum value conversions. This enables you to use - enum backed properties as route parameters. When an enum backed property is - used in routing, the enum's `value` or `name` will be used. -- Added `RedirectTrait`. This trait can be used to create custom redirect route - classes. - -### TestSuite - -- `assertRedirectBack()` added to assert a successful redirect has been made to the same previous URL. -- `assertRedirectBackToReferer()` added to assert a successful redirect has been made to the referer URL. -- `assertFlashMessageContains()` and `assertFlashMessageContainsAt()` were added. These methods enable - substring matching of flash message content. -- `TestFixture::$tableAlias` was added. This property lets you define the - table alias that will be used to load an `ORM\Table` instance for a fixture. - This improves compatibility for schemas that do not closely follow naming conventions. - -### Utility - -- `Text::uuid()` now supports configurable UUID generation. You can set a custom - UUID generator using `Configure::write('Text.uuidGenerator', $closure)` to - integrate your own UUID generation strategy or third-party libraries. - -### Validation - -- `ipOrRange()` validation has been added to check for an IP or a range (subnet). -- When validating within CakePHP marshalling context, the entity will be passed - into the `context` argument for use inside custom validation rules. This can - be useful when patching partially and then needing to get that data from the - entity instead of the passed data. -- `existsInNullable()` rule has been added. This rule allows `null` values - in nullable composite foreign keys, which is semantically correct for optional - relationships. Use `$rules->existsInNullable(['author_id', 'site_id'], 'SiteAuthors')` instead of `existsIn()` when you want to permit null values - in foreign keys. - -### View - -- `HtmlHelper::scriptStart()` and `scriptEnd()` now allow simple - wrapping script tags (``) around inline JavaScript. This - enables syntax highlighting in many editors to work. The wrapping script tag - will be removed and replaced with a script tag generated by the helper. -- `FormHelper` now supports a new option `nestedCheckboxAndRadio`. - By default, the helper generates inputs of type checkbox and radio nested - inside their label. Setting the `nestedCheckboxAndRadio` option to `false` - will turn off the nesting. -- `ViewBuilder::setConfigMergeStrategy()` was added to control how view options - are merged with the View class's default configuration. Available strategies are - `ViewBuilder::MERGE_DEEP` (recursive merge, default) and `ViewBuilder::MERGE_SHALLOW` - (simple array merge). This is useful when you want to replace array values in view - options rather than deep merging them. -- `ViewBuilder::getConfigMergeStrategy()` was added to retrieve the current merge - strategy setting. -- `PaginatorHelper::limitControl()` now automatically respects the - `maxLimit` configuration from the paginator, filtering out any limit options - that exceed it. A new `steps` option was added to automatically generate limit - options in multiples of a specific value (e.g., `['steps' => 10]` generates - 10, 20, 30... up to maxLimit). -- `BreadcrumbsHelper::addMany()` and `BreadcrumbsHelper::prependMany()` - were added. These methods allow adding multiple breadcrumbs at once with support - for shared options that apply to all crumbs. diff --git a/docs/en/appendices/5-4-migration-guide.md b/docs/en/appendices/5-4-migration-guide.md deleted file mode 100644 index 562d3d79ff..0000000000 --- a/docs/en/appendices/5-4-migration-guide.md +++ /dev/null @@ -1,32 +0,0 @@ -# 5.4 Migration Guide - -The 5.4.0 release is backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. - -## Upgrade Tool - -The [upgrade tool](../appendices/migration-guides) provides rector rules for -automating some of the migration work. Run rector before updating your -`composer.json` dependencies: - -```text -bin/cake upgrade rector --rules cakephp54 -``` - -## Behavior Changes - -- WIP - -## Deprecations - -- WIP - -## New Features - -### Utility - -- New `Cake\Utility\Fs\Finder` class provides a fluent, iterator-based API for - discovering files and directories with support for pattern matching, depth - control, and custom filters. The `Cake\Utility\Fs\Path` class offers - cross-platform utilities for path manipulation. diff --git a/docs/en/appendices/6-0-migration-guide.md b/docs/en/appendices/6-0-migration-guide.md index 71ee4e0fe7..68aab2c3d1 100644 --- a/docs/en/appendices/6-0-migration-guide.md +++ b/docs/en/appendices/6-0-migration-guide.md @@ -1,7 +1,7 @@ # 6.0 Migration Guide CakePHP 6.0 contains breaking changes, and is not backwards compatible with 5.x. -Before attempting to upgrade to 6.0 first upgrade to 5.2+ and resolve all +Before attempting to upgrade to 6.0 first upgrade to 5.3+ and resolve all deprecation warnings. ## Behavior Changes @@ -27,60 +27,47 @@ You can find a full list of adjusted methods in the [cakephp/upgrade tool](https Some methods have also been renamed to better reflect their purpose. These are: -- `Cake\Console\ConsoleOutput` - - `_write()` has been renamed to `writeStream()` +| Class | Old Method(s) | New Method(s) | +|---------------------------------------|-----------------------------------------------------|--------------------------------------------------------| +| `Cake\Console\ConsoleOutput` | `_write()` | `writeStream()` | +| `Cake\Form\Form` | `_execute()` | `process()` | +| `Cake\Http\Client` | `_sendRequest()` | `processRequest()` | +| `Cake\Http\ServerRequest` | `_is()` | `isType()` | +| `Cake\Http\Client\Adapter\Stream` | `_send()` | `processRequest()` | +| `Cake\I18n\DateFormatTrait` | `_parseDateTime()` | `processDateTime()` | +| `Cake\Mailer\Transport\SmtpTransport` | `_connect()`
`_disconnect()` | `connectSmtp()`
`disconnectSmtp()` | +| `Cake\ORM\Table` | `_saveMany()`
`_deleteMany()` | `doSaveMany()`
`doDeleteMany()` | +| `Cake\ORM\Behavior\TreeBehavior` | `_moveUp()`
`_moveDown()`
`_removeFromTree()` | `doMoveUp()`
`doMoveDown()`
`doRemoveFromTree()` | +| `Cake\ORM\Association\HasMany` | `_unlink()` | `doUnlink()` | +| `Cake\ORM\Query\SelectQuery` | `_decorateResults()`
`_execute()` | `ormDecorateResults()`
`ormExecute()` | +| `Cake\Utility\Hash` | `_filter()`
`_merge()` | `doFilter()`
`doMerge()` | +| `Cake\Utility\Text` | `_wordWrap()` | `doWordWrap()` | +| `Cake\Utility\Xml` | `_fromArray()`
`_toArray()` | `doFromArray()`
`doToArray()` | +| `Cake\View\View` | `_render()` | `renderFile()` | +| `Cake\View\Helper\PaginatorHelper` | `_numbers()` | `buildNumbers()` | -- `Cake\Form\Form` - - `_execute()` has been renamed to `process()` +### Renamed Properties -- `Cake\Http\Client` - - `_sendRequest()` has been renamed to `processRequest()` +Properties starting with a `_` have been renamed to **remove the leading underscore**. +You can find a full list of adjusted properties in the +[cakephp/upgrade tool](https://github.com/cakephp/upgrade/blob/6.x/config/rector/sets/cakephp60.php). -- `Cake\Http\ServerRequest` - - `_is()` has been renamed to `isType()` +Some properties have also been renamed to better reflect their purpose. These are: -- `Cake\Http\Client\Adapter\Stream` - - `_send()` has been renamed to `processRequest()` +- `Cake\ORM\Entity` + - `$_accessible` has been renamed to `patchable` +- `Cake\View\View` + - `$_helpers` has been renamed to `helperRegistry` as `$helpers` already exists to hold the configuration -- `Cake\I18n\DateFormatTrait` - - `_parseDateTime()` has been renamed to `processDateTime()` +### Console -- `Cake\Mailer\Transport\SmtpTransport` - - `_connect()` has been renamed to `connectSmtp()` - - `_disconnect()` has been renamed to `disconnectSmtp()` +- The `validChoice` method on `ConsoleInputArgument` and `ConsoleInputOption` has been renamed to `validateChoice`. -- `Cake\ORM\Table` - - `_saveMany()` has been renamed to `doSaveMany()` - - `_deleteMany()` has been renamed to `doDeleteMany()` +### Command -- `Cake\ORM\Behavior\TreeBehavior` - - `_moveUp()` has been renamed to `doMoveUp()` - - `_moveDown()` has been renamed to `doMoveDown()` - - `_removeFromTree()` has been renamed to `doRemoveFromTree()` - -- `Cake\ORM\Association\HasMany` - - `_unlink()` has been renamed to `doUnlink()` - -- `Cake\ORM\Query\SelectQuery` - - `_decorateResults()` has been renamed to `ormDecorateResults()` - - `_execute()` has been renamed to `ormExecute()` - -- `Cake\Utility\Hash` - - `_filter()` has been renamed to `doFilter()` - - `_merge()` has been renamed to `doMerge()` - -- `Cake\Utility\Text` - - `_wordWrap()` has been renamed to `doWordWrap()` - -- `Cake\Utility\Xml` - - `_fromArray()` has been renamed to `doFromArray()` - - `_toArray()` has been renamed to `doToArray()` - -- `Cake\View\View` - - `_render()` has been renamed to `renderFile()` - -- `Cake\View\Helper\PaginatorHelper` - - `_numbers()` has been renamed to `buildNumbers()` +- Command args and io are now available as properties instead of them being + passed down to the execute method. See the current state of + [Commands](../console-commands/commands.md) for examples. ### Event @@ -112,6 +99,12 @@ Some methods have also been renamed to better reflect their purpose. These are: - The `accessibleFields` option used in e.g. ORM Queries has been renamed to `patchableFields`. +### Router + +- `RouteBuilder` has adjusted the signature of `scope()`, `prefix()` and `resources()` + so the 2nd and 3rd parameters have been swapped. + We recommend using named parameters to prevent confusion and make the code more readable. + ### Utility - The default placeholder format for `Text::insert()` has been changed. diff --git a/docs/en/appendices/6-0-upgrade-guide.md b/docs/en/appendices/6-0-upgrade-guide.md index 9fba6afd74..1ad1c21ea5 100644 --- a/docs/en/appendices/6-0-upgrade-guide.md +++ b/docs/en/appendices/6-0-upgrade-guide.md @@ -6,7 +6,7 @@ First, check that your application is running on latest CakePHP 5.x version. Once your application is running on latest CakePHP 5.x, enable deprecation warnings in **config/app.php**: -``` text +```text 'Error' => [ 'errorLevel' => E_ALL, ] @@ -35,7 +35,7 @@ If you are not running on **PHP 8.4 or higher**, you will need to upgrade PHP be To help expedite fixing tedious changes there is an upgrade CLI tool: -``` bash +```bash # Install the upgrade tool git clone https://github.com/cakephp/upgrade cd upgrade @@ -46,7 +46,7 @@ composer install --no-dev With the upgrade tool installed you can now run it on your application or plugin: -``` text +```bash bin/cake upgrade rector --rules cakephp60 ``` diff --git a/docs/en/appendices/migration-guides.md b/docs/en/appendices/migration-guides.md index 82738cd439..cd33d4b30d 100644 --- a/docs/en/appendices/migration-guides.md +++ b/docs/en/appendices/migration-guides.md @@ -21,11 +21,11 @@ To use the upgrade tool: # Install the upgrade tool git clone https://github.com/cakephp/upgrade cd upgrade -git checkout 5.x +git checkout 6.x composer install --no-dev # Run rector with the desired ruleset -bin/cake upgrade rector --rules cakephp51 +bin/cake upgrade rector --rules cakephp60 ``` Run rector before updating your `composer.json` dependencies @@ -33,9 +33,3 @@ to ensure the tool can resolve class names correctly. - [6 0 Upgrade Guide](6-0-upgrade-guide) - [6 0 Migration Guide](6-0-migration-guide) -- [5 0 Upgrade Guide](5-0-upgrade-guide) -- [5 0 Migration Guide](5-0-migration-guide) -- [5 1 Migration Guide](5-1-migration-guide) -- [5 2 Migration Guide](5-2-migration-guide) -- [5 3 Migration Guide](5-3-migration-guide) -- [5 4 Migration Guide](5-4-migration-guide) diff --git a/docs/en/console-commands/commands.md b/docs/en/console-commands/commands.md index 55cb26ebd8..4c17d46782 100644 --- a/docs/en/console-commands/commands.md +++ b/docs/en/console-commands/commands.md @@ -27,9 +27,9 @@ use Cake\Console\ConsoleIo; class HelloCommand extends Command { - public function execute(Arguments $args, ConsoleIo $io): int + public function execute(): int { - $io->out('Hello world.'); + $this->io->out('Hello world.'); return static::CODE_SUCCESS; } @@ -71,10 +71,10 @@ class HelloCommand extends Command return $parser; } - public function execute(Arguments $args, ConsoleIo $io): int + public function execute(): int { - $name = $args->getArgument('name'); - $io->out("Hello {$name}."); + $name = $this->args->getArgument('name'); + $this->io->out("Hello {$name}."); return static::CODE_SUCCESS; } @@ -131,13 +131,13 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption return $parser; } -public function execute(Arguments $args, ConsoleIo $io): int +public function execute(): int { - $name = $args->getArgument('name'); - if ($args->getOption('yell')) { + $name = $this->args->getArgument('name'); + if ($this->args->getOption('yell')) { $name = mb_strtoupper($name); } - $io->out("Hello {$name}."); + $this->io->out("Hello {$name}."); return static::CODE_SUCCESS; } @@ -187,12 +187,12 @@ class UserCommand extends Command return $parser; } - public function execute(Arguments $args, ConsoleIo $io): int + public function execute(): int { - $name = $args->getArgument('name'); + $name = $this->args->getArgument('name'); $user = $this->fetchTable()->findByUsername($name)->first(); - $io->out(print_r($user, true)); + $this->io->out(print_r($user, true)); return static::CODE_SUCCESS; } @@ -209,12 +209,12 @@ to terminate execution: ```php // ... -public function execute(Arguments $args, ConsoleIo $io): int +public function execute(): int { - $name = $args->getArgument('name'); + $name = $this->args->getArgument('name'); if (mb_strlen($name) < 5) { // Halt execution, output to stderr, and set exit code to 1 - $io->error('Name must be at least 4 characters long.'); + $this->io->error('Name must be at least 4 characters long.'); $this->abort(); } @@ -222,15 +222,15 @@ public function execute(Arguments $args, ConsoleIo $io): int } ``` -You can also use `abort()` on the `$io` object to emit a message and code: +You can also use `abort()` on the `$this->io` object to emit a message and code: ```php -public function execute(Arguments $args, ConsoleIo $io): int +public function execute(): int { - $name = $args->getArgument('name'); + $name = $this->args->getArgument('name'); if (mb_strlen($name) < 5) { // Halt execution, output to stderr, and set exit code to 99 - $io->abort('Name must be at least 4 characters long.', 99); + $this->io->abort('Name must be at least 4 characters long.', 99); } return static::CODE_SUCCESS; @@ -354,9 +354,9 @@ The `TreeHelper` outputs an array as a tree structure. This is useful for displaying filesystem directories or any hierarchical data: ```php -public function execute(Arguments $args, ConsoleIo $io): int +public function execute(): int { - $helper = $io->helper('Tree'); + $helper = $this->io->helper('Tree'); $helper->output([ 'src' => [ 'Controller', @@ -461,9 +461,9 @@ class UpdateTableCommand extends Command return $parser; } - public function execute(Arguments $args, ConsoleIo $io): int + public function execute(): int { - $table = $args->getArgument('table'); + $table = $this->args->getArgument('table'); $this->fetchTable($table)->updateQuery() ->set([ 'modified' => new DateTime(), @@ -561,11 +561,11 @@ class UpdateTableCommand extends Command return $parser; } - public function execute(Arguments $args, ConsoleIo $io): int + public function execute(): int { - $table = $args->getArgument('table'); - if ($io->ask('Are you sure?', 'n', ['y', 'n']) !== 'y') { - $io->error('You need to be sure.'); + $table = $this->args->getArgument('table'); + if ($this->io->ask('Are you sure?', 'n', ['y', 'n']) !== 'y') { + $this->io->error('You need to be sure.'); $this->abort(); } $this->fetchTable($table)->updateQuery() diff --git a/docs/en/development/routing.md b/docs/en/development/routing.md index 208cdf46a9..166042e52d 100644 --- a/docs/en/development/routing.md +++ b/docs/en/development/routing.md @@ -24,7 +24,7 @@ this to your **config/routes.php** file: ```php /** @var \Cake\Routing\RouteBuilder $routes */ -$routes->connect('/', ['controller' => 'Articles', 'action' => 'index']); +$routes->connect(route: '/', defaults: ['controller' => 'Articles', 'action' => 'index']); ``` This will execute the index method in the `ArticlesController` when the @@ -33,7 +33,7 @@ accept multiple parameters, this would be the case, for example of a route for viewing an article's content: ```php -$routes->connect('/articles/*', ['controller' => 'Articles', 'action' => 'view']); +$routes->connect(route: '/articles/*', defaults: ['controller' => 'Articles', 'action' => 'view']); ``` The above route will accept any URL looking like `/articles/15` and invoke the @@ -44,17 +44,17 @@ you wish, you can restrict some parameters to conform to a regular expression: ```php // Using fluent interface $routes->connect( - '/articles/{id}', - ['controller' => 'Articles', 'action' => 'view'], + route: '/articles/{id}', + defaults: ['controller' => 'Articles', 'action' => 'view'], ) ->setPatterns(['id' => '\d+']) ->setPass(['id']); // Using options array $routes->connect( - '/articles/{id}', - ['controller' => 'Articles', 'action' => 'view'], - ['id' => '\d+', 'pass' => ['id']], + route: '/articles/{id}', + defaults: ['controller' => 'Articles', 'action' => 'view'], + options: ['id' => '\d+', 'pass' => ['id']], ); ``` @@ -83,9 +83,9 @@ parameters: ```php // In routes.php $routes->connect( - '/upgrade', - ['controller' => 'Subscriptions', 'action' => 'create'], - ['_name' => 'upgrade'], + route: '/upgrade', + defaults: ['controller' => 'Subscriptions', 'action' => 'create'], + options: ['_name' => 'upgrade'], ); use Cake\Routing\Router; @@ -101,9 +101,9 @@ connected inside a scope will inherit the path/defaults from their wrapping scopes: ```php -$routes->scope('/blog', ['plugin' => 'Blog'], function (RouteBuilder $routes) { - $routes->connect('/', ['controller' => 'Articles']); -}); +$routes->scope(path: '/blog', callback: function (RouteBuilder $routes) { + $routes->connect(route: '/', defaults: ['controller' => 'Articles']); +}, params: ['plugin' => 'Blog']); ``` The above route would match `/blog/` and send it to @@ -128,9 +128,9 @@ some routes we'll use the `scope()` method: use Cake\Routing\RouteBuilder; use Cake\Routing\Route\DashedRoute; -$routes->scope('/', function (RouteBuilder $routes) { +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { // Connect the generic fallback routes. - $routes->fallbacks(DashedRoute::class); + $routes->fallbacks(routeClass: DashedRoute::class); }); ``` @@ -143,15 +143,15 @@ The basic format for a route definition is: ```php $routes->connect( - '/url/template', - ['targetKey' => 'targetValue'], - ['option' => 'matchingRegex'], + route: '/url/template', + defaults: ['targetKey' => 'targetValue'], + options: ['option' => 'matchingRegex'], ); ``` The first parameter is used to tell the router what sort of URL you're trying to control. The URL is a normal slash delimited string, but can also contain -a wildcard (*) or [Route Elements](#route-elements). Using a wildcard tells the router +a wildcard `*` or [Route Elements](#route-elements). Using a wildcard tells the router that you are willing to accept any additional arguments supplied. Routes without a `*` only match the exact template pattern supplied. @@ -163,17 +163,17 @@ as a destination string. A few examples of route targets are: ```php // Array target to an application controller $routes->connect( - '/users/view/*', - ['controller' => 'Users', 'action' => 'view'] + route: '/users/view/*', + defaults: ['controller' => 'Users', 'action' => 'view'] ); -$routes->connect('/users/view/*', 'Users::view'); +$routes->connect(route: '/users/view/*', defaults: 'Users::view'); // Array target to a prefixed plugin controller $routes->connect( - '/admin/cms/articles', - ['prefix' => 'Admin', 'plugin' => 'Cms', 'controller' => 'Articles', 'action' => 'index'] + route: '/admin/cms/articles', + defaults: ['prefix' => 'Admin', 'plugin' => 'Cms', 'controller' => 'Articles', 'action' => 'index'] ); -$routes->connect('/admin/cms/articles', 'Cms.Admin/Articles::index'); +$routes->connect(route: '/admin/cms/articles', defaults: 'Cms.Admin/Articles::index'); ``` The first route we connect matches URLs starting with `/users/view` and maps @@ -212,8 +212,8 @@ when you want to use an argument that included a `/` in it: ```php $routes->connect( - '/pages/**', - ['controller' => 'Pages', 'action' => 'show'] + route: '/pages/**', + defaults: ['controller' => 'Pages', 'action' => 'show'] ); ``` @@ -225,8 +225,8 @@ compose the default route parameters: ```php $routes->connect( - '/government', - ['controller' => 'Pages', 'action' => 'display', 5], + route: '/government', + defaults: ['controller' => 'Pages', 'action' => 'display', 5], ); ``` @@ -242,7 +242,8 @@ that: ```php $routes->connect( - '/cooks/{action}/*', ['controller' => 'Users'] + route: '/cooks/{action}/*', + defaults: ['controller' => 'Users'] ); ``` @@ -267,16 +268,16 @@ specific HTTP verbs simpler: ```php // Create a route that only responds to GET requests. $routes->get( - '/cooks/{id}', - ['controller' => 'Users', 'action' => 'view'], - 'users:view', + template: '/cooks/{id}', + target: ['controller' => 'Users', 'action' => 'view'], + name: 'users:view', ); // Create a route that only responds to PUT requests $routes->put( - '/cooks/{id}', - ['controller' => 'Users', 'action' => 'update'], - 'users:update', + template: '/cooks/{id}', + target: ['controller' => 'Users', 'action' => 'update'], + name: 'users:update', ); ``` @@ -308,14 +309,14 @@ will be treated as part of the parameter: ```php $routes->connect( - '/{controller}/{id}', - ['action' => 'view'], + route: '/{controller}/{id}', + defaults: ['action' => 'view'], )->setPatterns(['id' => '[0-9]+']); $routes->connect( - '/{controller}/{id}', - ['action' => 'view'], - ['id' => '[0-9]+'], + route: '/{controller}/{id}', + defaults: ['action' => 'view'], + options: ['id' => '[0-9]+'], ); ``` @@ -336,15 +337,15 @@ rewritten like so: use Cake\Routing\Route\DashedRoute; // Create a builder with a different route class. -$routes->scope('/', function (RouteBuilder $routes) { - $routes->setRouteClass(DashedRoute::class); - $routes->connect('/{controller}/{id}', ['action' => 'view']) +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->setRouteClass(routeClass: DashedRoute::class); + $routes->connect(route: '/{controller}/{id}', defaults: ['action' => 'view']) ->setPatterns(['id' => '[0-9]+']); $routes->connect( - '/{controller}/{id}', - ['action' => 'view'], - ['id' => '[0-9]+'], + route: '/{controller}/{id}', + defaults: ['action' => 'view'], + options: ['id' => '[0-9]+'], ); }); ``` @@ -367,7 +368,7 @@ e.g have URLs like `/demo` instead of `/home/demo`, you can do the following: ```php -$routes->connect('/{action}', ['controller' => 'Home']); +$routes->connect(route: '/{action}', defaults: ['controller' => 'Home']); ``` If you would like to provide a case insensitive URL, you can use regular @@ -375,8 +376,8 @@ expression inline modifiers: ```php $routes->connect( - '/{userShortcut}', - ['controller' => 'Teachers', 'action' => 'profile', 1], + route: '/{userShortcut}', + defaults: ['controller' => 'Teachers', 'action' => 'profile', 1], )->setPatterns(['userShortcut' => '(?i:principal)']); ``` @@ -384,8 +385,8 @@ One more example, and you'll be a routing pro: ```php $routes->connect( - '/{controller}/{year}/{month}/{day}', - ['action' => 'index'], + route: '/{controller}/{year}/{month}/{day}', + defaults: ['action' => 'index'], )->setPatterns([ 'year' => '[12][0-9]{3}', 'month' => '0[1-9]|1[012]', @@ -448,8 +449,8 @@ of `connect()`: ```php $routes->connect( - '/{lang}/articles/{slug}', - ['controller' => 'Articles', 'action' => 'view'], + route: '/{lang}/articles/{slug}', + defaults: ['controller' => 'Articles', 'action' => 'view'], ) // Allow GET and POST requests. ->setMethods(['GET', 'POST']) @@ -481,7 +482,7 @@ same options (like `_host`, `_https`, or `_port`) to multiple routes without repeating them: ```php -$routes->scope('/api', function (RouteBuilder $routes) { +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { // Set default options for all routes in this scope $routes->setOptions([ '_host' => 'api.example.com', @@ -489,8 +490,8 @@ $routes->scope('/api', function (RouteBuilder $routes) { ]); // These routes will automatically have _host and _https set - $routes->get('/users', ['controller' => 'Users', 'action' => 'index']); - $routes->get('/posts', ['controller' => 'Posts', 'action' => 'index']); + $routes->get(template: '/users', target: ['controller' => 'Users', 'action' => 'index']); + $routes->get(template: '/posts', target: ['controller' => 'Posts', 'action' => 'index']); }); ``` @@ -503,23 +504,23 @@ Options set via `setOptions()` are: Example with nested scopes and overrides: ```php -$routes->scope('/api', function (RouteBuilder $routes) { +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { $routes->setOptions(['_host' => 'api.example.com']); // This route uses the default host - $routes->get('/public', ['controller' => 'Public', 'action' => 'index']); + $routes->get(template: '/public', target: ['controller' => 'Public', 'action' => 'index']); // This route overrides the default host - $routes->get('/internal', [ + $routes->get(template: '/internal', target: [ 'controller' => 'Internal', 'action' => 'index', '_host' => 'internal.example.com', ]); // Nested scope inherits the default host - $routes->scope('/v2', function (RouteBuilder $routes) { + $routes->scope(path: '/v2', callback: function (RouteBuilder $routes) { // This also uses api.example.com - $routes->get('/users', ['controller' => 'Users', 'action' => 'index']); + $routes->get(template: '/users', target: ['controller' => 'Users', 'action' => 'index']); }); }); ``` @@ -545,10 +546,10 @@ public function view($articleId = null, $slug = null) } // routes.php -$routes->scope('/', function (RouteBuilder $routes) { +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { $routes->connect( - '/blog/{id}-{slug}', // For example, /blog/3-CakePHP_Rocks - ['controller' => 'Blogs', 'action' => 'view'], + route: '/blog/{id}-{slug}', // For example, /blog/3-CakePHP_Rocks + defaults: ['controller' => 'Blogs', 'action' => 'view'], ) // Define the route elements in the route template // to prepend as function arguments. Order matters as this @@ -615,16 +616,16 @@ option can be used in reverse routing to identify the route you want to use: ```php // Connect a route with a name. $routes->connect( - '/login', - ['controller' => 'Users', 'action' => 'login'], - ['_name' => 'login'], + route: '/login', + defaults: ['controller' => 'Users', 'action' => 'login'], + options: ['_name' => 'login'], ); // Name a verb specific route $routes->post( - '/logout', - ['controller' => 'Users', 'action' => 'logout'], - 'logout', + template: '/logout', + target: ['controller' => 'Users', 'action' => 'logout'], + name: 'logout', ); // Generate a URL using a named route. @@ -648,34 +649,34 @@ for the route names. CakePHP makes building up route names easier by allowing you to define name prefixes in each scope: ```php -$routes->scope('/api', ['_namePrefix' => 'api:'], function (RouteBuilder $routes) { +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { // This route's name will be `api:ping` - $routes->get('/ping', ['controller' => 'Pings'], 'ping'); -}); + $routes->get(template: '/ping', target: ['controller' => 'Pings'], name: 'ping'); +}, params: ['_namePrefix' => 'api:']); // Generate a URL for the ping route Router::url(['_name' => 'api:ping']); // Use namePrefix with plugin() -$routes->plugin('Contacts', ['_namePrefix' => 'contacts:'], function (RouteBuilder $routes) { +$routes->plugin(name: 'Contacts', callback: function (RouteBuilder $routes) { // Connect routes. -}); +}, params: ['_namePrefix' => 'contacts:']); // Or with prefix() -$routes->prefix('Admin', ['_namePrefix' => 'admin:'], function (RouteBuilder $routes) { +$routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { // Connect routes. -}); +}, params: ['_namePrefix' => 'admin:']); ``` You can also use the `_namePrefix` option inside nested scopes and it works as you'd expect: ```php -$routes->plugin('Contacts', ['_namePrefix' => 'contacts:'], function (RouteBuilder $routes) { - $routes->scope('/api', ['_namePrefix' => 'api:'], function (RouteBuilder $routes) { +$routes->plugin(name: 'Contacts', callback: function (RouteBuilder $routes) { + $routes->scope(path: '/api', callback: function (RouteBuilder $routes) { // This route's name will be `contacts:api:ping` - $routes->get('/ping', ['controller' => 'Pings'], 'ping'); - }); -}); + $routes->get(template: '/ping', target: ['controller' => 'Pings'], name: 'ping'); + }, params: ['_namePrefix' => 'api:']); +}, params: ['_namePrefix' => 'contacts:']); // Generate a URL for the ping route Router::url(['_name' => 'contacts:api:ping']); @@ -696,11 +697,11 @@ can be enabled by using the `prefix` scope method: ```php use Cake\Routing\Route\DashedRoute; -$routes->prefix('Admin', function (RouteBuilder $routes) { +$routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { // All routes here will be prefixed with `/admin`, and // have the `'prefix' => 'Admin'` route element added that // will be required when generating URLs for these routes - $routes->fallbacks(DashedRoute::class); + $routes->fallbacks(routeClass: DashedRoute::class); }); ``` @@ -717,11 +718,11 @@ You can map the URL /admin to your `index()` action of pages controller using following route: ```php -$routes->prefix('Admin', function (RouteBuilder $routes) { +$routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { // Because you are in the admin scope, // you do not need to include the /admin prefix // or the Admin route element. - $routes->connect('/', ['controller' => 'Pages', 'action' => 'index']); + $routes->connect(route: '/', defaults: ['controller' => 'Pages', 'action' => 'index']); }); ``` @@ -729,11 +730,11 @@ When creating prefix routes, you can set additional route parameters using the `$options` argument: ```php -$routes->prefix('Admin', ['param' => 'value'], function (RouteBuilder $routes) { +$routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { // Routes connected here are prefixed with '/admin' and // have the 'param' routing key set. - $routes->connect('/{controller}'); -}); + $routes->connect(route: '/{controller}'); +}, params: ['param' => 'value']); ``` Note the additional route parameters will be added to all the connected routes defined @@ -745,18 +746,18 @@ would be mapped to `my-prefix` in the URL. Make sure to set a path for such pref if you want to use a different format like for example underscoring: ```php -$routes->prefix('MyPrefix', ['path' => '/my_prefix'], function (RouteBuilder $routes) { +$routes->prefix(name: 'MyPrefix', callback: function (RouteBuilder $routes) { // Routes connected here are prefixed with '/my_prefix' - $routes->connect('/{controller}'); -}); + $routes->connect(route: '/{controller}'); +}, params: ['path' => '/my_prefix']); ``` You can define prefixes inside plugin scopes as well: ```php -$routes->plugin('DebugKit', function (RouteBuilder $routes) { - $routes->prefix('Admin', function (RouteBuilder $routes) { - $routes->connect('/{controller}'); +$routes->plugin(name: 'DebugKit', callback: function (RouteBuilder $routes) { + $routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { + $routes->connect(route: '/{controller}'); }); }); ``` @@ -767,9 +768,9 @@ The connected route would have the `plugin` and `prefix` route elements set. When defining prefixes, you can nest multiple prefixes if necessary: ```php -$routes->prefix('Manager', function (RouteBuilder $routes) { - $routes->prefix('Admin', function (RouteBuilder $routes) { - $routes->connect('/{controller}/{action}'); +$routes->prefix(name: 'Manager', callback: function (RouteBuilder $routes) { + $routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { + $routes->connect(route: '/{controller}/{action}'); }); }); ``` @@ -835,10 +836,10 @@ Routes for [Plugins](../plugins) should be created using the `plugin()` method. This method creates a new routing scope for the plugin's routes: ```php -$routes->plugin('DebugKit', function (RouteBuilder $routes) { +$routes->plugin(name: 'DebugKit', callback: function (RouteBuilder $routes) { // Routes connected here are prefixed with '/debug-kit' and // have the plugin route element set to 'DebugKit'. - $routes->connect('/{controller}'); + $routes->connect(route: '/{controller}'); }); ``` @@ -846,19 +847,19 @@ When creating plugin scopes, you can customize the path element used with the `path` option: ```php -$routes->plugin('DebugKit', ['path' => '/debugger'], function (RouteBuilder $routes) { +$routes->plugin(name: 'DebugKit', options: ['path' => '/debugger'], callback: function (RouteBuilder $routes) { // Routes connected here are prefixed with '/debugger' and // have the plugin route element set to 'DebugKit'. - $routes->connect('/{controller}'); + $routes->connect(route: '/{controller}'); }); ``` When using scopes you can nest plugin scopes within prefix scopes: ```php -$routes->prefix('Admin', function (RouteBuilder $routes) { - $routes->plugin('DebugKit', function (RouteBuilder $routes) { - $routes->connect('/{controller}'); +$routes->prefix(name: 'Admin', callback: function (RouteBuilder $routes) { + $routes->plugin(name: 'DebugKit', callback: function (RouteBuilder $routes) { + $routes->connect(route: '/{controller}'); }); }); ``` @@ -906,8 +907,8 @@ with the following router connection: ```php use Cake\Routing\Route\DashedRoute; -$routes->plugin('ToDo', ['path' => 'to-do'], function (RouteBuilder $routes) { - $routes->fallbacks(DashedRoute::class); +$routes->plugin(name: 'ToDo', options: ['path' => 'to-do'], callback: function (RouteBuilder $routes) { + $routes->fallbacks(routeClass: DashedRoute::class); }); ``` @@ -916,17 +917,17 @@ $routes->plugin('ToDo', ['path' => 'to-do'], function (RouteBuilder $routes) { Routes can match specific HTTP methods using the HTTP verb helper methods: ```php -$routes->scope('/', function (RouteBuilder $routes) { +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { // This route only matches on POST requests. $routes->post( - '/reviews/start', - ['controller' => 'Reviews', 'action' => 'start'], + template: '/reviews/start', + target: ['controller' => 'Reviews', 'action' => 'start'], ); // Match multiple verbs $routes->connect( - '/reviews/start', - [ + route: '/reviews/start', + defaults: [ 'controller' => 'Reviews', 'action' => 'start', ] @@ -953,17 +954,17 @@ Routes can use the `_host` option to only match specific hosts. You can use the `*.` wildcard to match any subdomain: ```php -$routes->scope('/', function (RouteBuilder $routes) { +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { // This route only matches on http://images.example.com $routes->connect( - '/images/default-logo.png', - ['controller' => 'Images', 'action' => 'default'], + route: '/images/default-logo.png', + defaults: ['controller' => 'Images', 'action' => 'default'], )->setHost('images.example.com'); // This route only matches on http://*.example.com $routes->connect( - '/images/old-logo.png', - ['controller' => 'Images', 'action' => 'oldLogo'] + route: '/images/old-logo.png', + defaults: ['controller' => 'Images', 'action' => 'oldLogo'] )->setHost('*.example.com'); }); ``` @@ -976,8 +977,8 @@ parameter when generating URLs: ```php // If you have this route $routes->connect( - '/images/old-logo.png', - ['controller' => 'Images', 'action' => 'oldLogo'], + route: '/images/old-logo.png', + defaults: ['controller' => 'Images', 'action' => 'oldLogo'], )->setHost('images.example.com'); // You need this to generate a url @@ -998,8 +999,8 @@ To handle different file extensions in your URLs, you can define the extensions using the `Cake\Routing\RouteBuilder::setExtensions()` method: ```php -$routes->scope('/', function (RouteBuilder $routes) { - $routes->setExtensions(['json', 'xml']); +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->setExtensions(extensions: ['json', 'xml']); }); ``` @@ -1020,11 +1021,11 @@ from the URL, and then parse what remains. If you want to create a URL such as /page/title-of-page.html you would create your route using: ```php -$routes->scope('/page', function (RouteBuilder $routes) { - $routes->setExtensions(['json', 'xml', 'html']); +$routes->scope(path: '/page', callback: function (RouteBuilder $routes) { + $routes->setExtensions(extensions: ['json', 'xml', 'html']); $routes->connect( - '/{title}', - ['controller' => 'Pages', 'action' => 'view'], + route: '/{title}', + defaults: ['controller' => 'Pages', 'action' => 'view'], )->setPass(['title']); }); ``` @@ -1057,18 +1058,18 @@ registered into the route collection: use Cake\Http\Middleware\CsrfProtectionMiddleware; use Cake\Http\Middleware\EncryptedCookieMiddleware; -$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware()); -$routes->registerMiddleware('cookies', new EncryptedCookieMiddleware()); +$routes->registerMiddleware(name: 'csrf', middleware: new CsrfProtectionMiddleware()); +$routes->registerMiddleware(name: 'cookies', middleware: new EncryptedCookieMiddleware()); ``` Once registered, scoped middleware can be applied to specific scopes: ```php -$routes->scope('/cms', function (RouteBuilder $routes) { +$routes->scope(path: '/cms', callback: function (RouteBuilder $routes) { // Enable CSRF & cookies middleware $routes->applyMiddleware('csrf', 'cookies'); - $routes->get('/articles/{action}/*', ['controller' => 'Articles']); + $routes->get(template: '/articles/{action}/*', target: ['controller' => 'Articles']); }); ``` @@ -1076,9 +1077,9 @@ In situations where you have nested scopes, inner scopes will inherit the middleware applied in the containing scope: ```php -$routes->scope('/api', function (RouteBuilder $routes) { +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { $routes->applyMiddleware('ratelimit', 'auth.api'); - $routes->scope('/v1', function (RouteBuilder $routes) { + $routes->scope(path: '/v1', callback: function (RouteBuilder $routes) { $routes->applyMiddleware('v1compat'); // Define routes here. }); @@ -1090,11 +1091,11 @@ In the above example, the routes defined in `/v1` will have 'ratelimit', middleware applied to routes in each scope will be isolated: ```php -$routes->scope('/blog', function (RouteBuilder $routes) { +$routes->scope(path: '/blog', callback: function (RouteBuilder $routes) { $routes->applyMiddleware('auth'); // Connect the authenticated actions for the blog here. }); -$routes->scope('/blog', function (RouteBuilder $routes) { +$routes->scope(path: '/blog', callback: function (RouteBuilder $routes) { // Connect the public actions for the blog here. }); ``` @@ -1110,10 +1111,10 @@ be combined into groups. Once combined groups can be applied like middleware can: ```php -$routes->registerMiddleware('cookie', new EncryptedCookieMiddleware()); -$routes->registerMiddleware('auth', new AuthenticationMiddleware()); -$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware()); -$routes->middlewareGroup('web', ['cookie', 'auth', 'csrf']); +$routes->registerMiddleware(name: 'cookie', middleware: new EncryptedCookieMiddleware()); +$routes->registerMiddleware(name: 'auth', middleware: new AuthenticationMiddleware()); +$routes->registerMiddleware(name: 'csrf', middleware: new CsrfProtectionMiddleware()); +$routes->middlewareGroup(name: 'web', middleware: ['cookie', 'auth', 'csrf']); // Apply the group $routes->applyMiddleware('web'); @@ -1130,9 +1131,9 @@ to allow REST access to a recipe controller, we'd do something like this: ```php // In config/routes.php... -$routes->scope('/', function (RouteBuilder $routes) { - $routes->setExtensions(['json']); - $routes->resources('Recipes'); +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->setExtensions(extensions: ['json']); + $routes->resources(name: 'Recipes'); }); ``` @@ -1152,7 +1153,7 @@ json and rss. These routes are HTTP Request Method sensitive. > [!NOTE] > The default for pattern for resource IDs only matches integers or UUIDs. > If your IDs are different you will have to supply a regular expression pattern -> via the `id` option, for example, `$builder->resources('Recipes', ['id' => '.*'])`. +> via the `id` option, for example, `$builder->resources(name: 'Recipes', options: ['id' => '.*'])`. The HTTP method being used is detected from a few different sources. The sources in order of preference are: @@ -1173,9 +1174,9 @@ sub-resources as well. Sub-resource routes will be prepended by the original resource name and a id parameter. For example: ```php -$routes->scope('/api', function (RouteBuilder $routes) { - $routes->resources('Articles', function (RouteBuilder $routes) { - $routes->resources('Comments'); +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'Articles', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'Comments'); }); }); ``` @@ -1199,9 +1200,9 @@ you have both nested and non-nested resource controllers you can use a different controller in each context by using prefixes: ```php -$routes->scope('/api', function (RouteBuilder $routes) { - $routes->resources('Articles', function (RouteBuilder $routes) { - $routes->resources('Comments', ['prefix' => 'Articles']); +$routes->scope(path: '/api', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'Articles', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'Comments', options: ['prefix' => 'Articles']); }); }); ``` @@ -1221,7 +1222,7 @@ By default, CakePHP will connect 6 routes for each resource. If you'd like to only connect specific resource routes you can use the `only` option: ```php -$routes->resources('Articles', [ +$routes->resources(name: 'Articles', options: [ 'only' => ['index', 'view'], ]); ``` @@ -1246,7 +1247,7 @@ routes. For example, if your `edit()` action is called `put()` you can use the `actions` key to rename the actions used: ```php -$routes->resources('Articles', [ +$routes->resources(name: 'Articles', options: [ 'actions' => ['update' => 'put', 'create' => 'add'], ]); ``` @@ -1259,7 +1260,7 @@ instead of `create()`. You can map additional resource methods using the `map` option: ```php -$routes->resources('Articles', [ +$routes->resources(name: 'Articles', options: [ 'map' => [ 'deleteAll' => [ 'action' => 'deleteAll', @@ -1276,7 +1277,7 @@ can use the 'path' key inside the resource definition to customize the path name: ```php -$routes->resources('Articles', [ +$routes->resources(name: 'Articles', options: [ 'map' => [ 'updateAll' => [ 'action' => 'updateAll', @@ -1297,7 +1298,7 @@ Resource routes can be connected to controllers in routing prefixes by connecting routes within a prefixed scope or by using the `prefix` option: ```php -$routes->resources('Articles', [ +$routes->resources(name: 'Articles', options: [ 'prefix' => 'Api', ]); ``` @@ -1310,8 +1311,8 @@ You can provide `connectOptions` key in the `$options` array for `resources()` to provide custom setting used by `connect()`: ```php -$routes->scope('/', function (RouteBuilder $routes) { - $routes->resources('Books', [ +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'Books', options: [ 'connectOptions' => [ 'routeClass' => 'ApiRoute', ] @@ -1328,8 +1329,8 @@ would be **/blog-posts**. You can specify an alternative inflection type using the `inflect` option: ```php -$routes->scope('/', function (RouteBuilder $routes) { - $routes->resources('BlogPosts', [ +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'BlogPosts', options: [ 'inflect' => 'underscore', // Will use ``Inflector::underscore()`` ]); }); @@ -1343,8 +1344,8 @@ By default, resource routes use an inflected form of the resource name for the URL segment. You can set a custom URL segment with the `path` option: ```php -$routes->scope('/', function (RouteBuilder $routes) { - $routes->resources('BlogPosts', ['path' => 'posts']); +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->resources(name: 'BlogPosts', options: ['path' => 'posts']); }); ``` @@ -1655,11 +1656,11 @@ header redirection if a match is found. The redirection can occur to a destination within your application or an outside location: ```php -$routes->scope('/', function (RouteBuilder $routes) { +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { $routes->redirect( - '/home/*', - ['controller' => 'Articles', 'action' => 'view'], - ['persist' => true] + route: '/home/*', + url: ['controller' => 'Articles', 'action' => 'view'], + options: ['persist' => true] // Or ['persist'=>['id']] for default routing where the // view action expects $id as an argument. ); @@ -1673,8 +1674,8 @@ redirected to. You can redirect to external locations using string URLs as the destination: ```php -$routes->scope('/', function (RouteBuilder $routes) { - $routes->redirect('/articles/*', 'http://google.com', ['status' => 302]); +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->redirect(route: '/articles/*', url: 'http://google.com', options: ['status' => 302]); }); ``` @@ -1690,9 +1691,9 @@ off with a route that looks like: ```php $routes->get( - '/view/{id}', - ['controller' => 'Articles', 'action' => 'view'], - 'articles:view', + template: '/view/{id}', + target: ['controller' => 'Articles', 'action' => 'view'], + name: 'articles:view', ); ``` @@ -1713,13 +1714,13 @@ rework when URLs require more parameters: use Cake\Routing\Route\EntityRoute; // Create entity routes for the rest of this scope. -$routes->setRouteClass(EntityRoute::class); +$routes->setRouteClass(routeClass: EntityRoute::class); // Create the route just like before. $routes->get( - '/view/{id}/{slug}', - ['controller' => 'Articles', 'action' => 'view'], - 'articles:view', + template: '/view/{id}/{slug}', + target: ['controller' => 'Articles', 'action' => 'view'], + name: 'articles:view', ); ``` @@ -1777,17 +1778,17 @@ option: ```php $routes->connect( - '/{slug}', - ['controller' => 'Articles', 'action' => 'view'], - ['routeClass' => 'SlugRoute'], + route: '/{slug}', + defaults: ['controller' => 'Articles', 'action' => 'view'], + options: ['routeClass' => 'SlugRoute'], ); // Or by setting the routeClass in your scope. -$routes->scope('/', function (RouteBuilder $routes) { - $routes->setRouteClass('SlugRoute'); +$routes->scope(path: '/', callback: function (RouteBuilder $routes) { + $routes->setRouteClass(routeClass: 'SlugRoute'); $routes->connect( - '/{slug}', - ['controller' => 'Articles', 'action' => 'view'], + route: '/{slug}', + defaults: ['controller' => 'Articles', 'action' => 'view'], ); }); ``` @@ -1808,7 +1809,7 @@ option for each route. For example using: ```php use Cake\Routing\Route\DashedRoute; -$routes->setRouteClass(DashedRoute::class); +$routes->setRouteClass(routeClass: DashedRoute::class); ``` will cause all routes connected after this to use the `DashedRoute` route class. @@ -1827,7 +1828,7 @@ Calling fallbacks like so: ```php use Cake\Routing\Route\DashedRoute; -$routes->fallbacks(DashedRoute::class); +$routes->fallbacks(routeClass: DashedRoute::class); ``` Is equivalent to the following explicit calls: @@ -1835,8 +1836,8 @@ Is equivalent to the following explicit calls: ```php use Cake\Routing\Route\DashedRoute; -$routes->connect('/{controller}', ['action' => 'index'], ['routeClass' => DashedRoute::class]); -$routes->connect('/{controller}/{action}/*', [], ['routeClass' => DashedRoute::class]); +$routes->connect(route: '/{controller}', defaults: ['action' => 'index'], options: ['routeClass' => DashedRoute::class]); +$routes->connect(route: '/{controller}/{action}/*', defaults: [], options: ['routeClass' => DashedRoute::class]); ``` > [!NOTE]