Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/en/appendices/5-4-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ The default eager loading strategy for `HasMany` and `BelongsToMany` association
has changed from `select` to `subquery`. If you need the previous behavior,
explicitly set `'strategy' => 'select'` when defining associations.

### Controller

Loading a component with the same alias as the controller's default table now
triggers a warning. See [Component Alias Conflicts](../controllers/components#component-alias-conflicts).

## Deprecations

- WIP
Expand Down Expand Up @@ -60,12 +65,24 @@ explicitly set `'strategy' => 'select'` when defining associations.
nested array format matching `contain()` syntax.
See [Converting Request Data into Entities](../orm/saving-data#converting-request-data-into-entities).

### Http

- Added PSR-13 Link implementation with `Cake\Http\Link\Link` and `Cake\Http\Link\LinkProvider`
classes for hypermedia link support. Links added to responses are automatically emitted
as HTTP `Link` headers. See [Hypermedia Links](../controllers/request-response#hypermedia-links).

### Utility

- Added `Cake\Utility\Fs\Finder` class for fluent file discovery with pattern matching,
depth control, and custom filters. Added `Cake\Utility\Fs\Path` for cross-platform
path manipulation.

### Collection

- Added [`keys()`](../core-libraries/collections#keys) and [`values()`](../core-libraries/collections#values) methods for extracting keys or re-indexing values.
- Added [`implode()`](../core-libraries/collections#implode) method to concatenate elements into a string.
- Added [`when()`](../core-libraries/collections#when) and [`unless()`](../core-libraries/collections#unless) methods for conditional method chaining.

### View

- Added `{{inputId}}` template variable to `inputContainer` and `error` templates
Expand Down
41 changes: 41 additions & 0 deletions docs/en/controllers/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,47 @@ class MyFlashComponent extends FlashComponent
The above would *alias* `MyFlashComponent` to `$this->Flash` in your
controllers.

### Component Alias Conflicts

When a component alias matches the controller's default table name, accessing
that property will return the table instead of the component. CakePHP triggers
a warning when this conflict is detected:

```php
class PaymentsController extends AppController
{
public function initialize(): void
{
parent::initialize();
// Warning: Component alias `Payments` clashes with the default table name
$this->loadComponent('Payments');
}

public function index()
{
// This returns PaymentsTable, not PaymentsComponent!
$this->Payments;
}
}
```

To resolve this conflict, either use a different component alias:

```php
$this->loadComponent('Payments', ['className' => 'Payments', 'alias' => 'PaymentService']);
// Access via $this->PaymentService
```

Or set `Controller::$defaultTable` to an empty string if the controller doesn't
need a default table:

```php
class PaymentsController extends AppController
{
protected ?string $defaultTable = '';
}
```

> [!NOTE]
> Aliasing a component replaces that instance anywhere that component is used,
> including inside other Components.
Expand Down
55 changes: 55 additions & 0 deletions docs/en/controllers/request-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,61 @@ You can now use the convenience method
`Cake\Http\Response::withLocation()` to directly set or get the
redirect location header.

### Hypermedia Links

`method` Cake\\Http\\Response::**withLink**(LinkInterface $link): static

CakePHP implements [PSR-13](https://www.php-fig.org/psr/psr-13/) for hypermedia
link support. You can add links to responses, and they will be automatically
emitted as HTTP `Link` headers when the response is sent:

```php
use Cake\Http\Link\Link;

// Add a simple link
$response = $response->withLink(new Link('/api/users/1', 'self'));

// Add a link with multiple relations and attributes
$response = $response->withLink(
(new Link('/api/users?page=2'))
->withRel('next')
->withAttribute('type', 'application/json'),
);

// Preload resources for performance
$response = $response->withLink(
(new Link('/css/app.css'))
->withRel('preload')
->withAttribute('as', 'style'),
);
```

The `Link` class implements `EvolvableLinkInterface` and provides these methods:

- `withHref(string $href)` - Set the link URI
- `withRel(string $rel)` - Add a link relation
- `withoutRel(string $rel)` - Remove a link relation
- `withAttribute(string $name, $value)` - Add an attribute
- `withoutAttribute(string $name)` - Remove an attribute

You can also work with multiple links using `LinkProvider`:

```php
use Cake\Http\Link\Link;
use Cake\Http\Link\LinkProvider;

$provider = new LinkProvider([
new Link('/api/users/1', 'self'),
new Link('/api/users?page=2', 'next'),
]);

// Get the link provider from a response
$links = $response->getLinks();

// Set a new link provider
$response = $response->withLinkProvider($provider);
```

### Setting the Body

`method` Cake\\Http\\Response::**withStringBody**(?string $string): static
Expand Down
108 changes: 99 additions & 9 deletions docs/en/core-libraries/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ application as well.
| `contains` | `countBy` | `each` |
| `every` | `extract` | `filter` |
| `first` | `firstMatch` | `groupBy` |
| `indexBy` | `insert` | `isEmpty` |
| `last` | `listNested` | `map` |
| `match` | `max` | `median` |
| `min` | `nest` | `prepend` |
| `prependItem` | `reduce` | `reject` |
| `sample` | `shuffle` | `skip` |
| `some` | `sortBy` | `stopWhen` |
| `sumOf` | `take` | `through` |
| `transpose` | `unfold` | `zip` |
| `implode` | `indexBy` | `insert` |
| `isEmpty` | `keys` | `last` |
| `listNested` | `map` | `match` |
| `max` | `median` | `min` |
| `nest` | `prepend` | `prependItem`|
| `reduce` | `reject` | `sample` |
| `shuffle` | `skip` | `some` |
| `sortBy` | `stopWhen` | `sumOf` |
| `take` | `through` | `transpose` |
| `unfold` | `unless` | `values` |
| `when` | `zip` | |

## Iterating

Expand Down Expand Up @@ -1431,3 +1433,91 @@ foreach ($ages as $age) {
}
}
```

## Transforming Keys and Values

### keys()

`method` Cake\\Collection\\Collection::**keys**(): CollectionInterface

Returns a new collection containing only the keys from the original collection:

```php
$collection = new Collection(['a' => 1, 'b' => 2, 'c' => 3]);
$keys = $collection->keys()->toList(); // ['a', 'b', 'c']
```

### values()

`method` Cake\\Collection\\Collection::**values**(): CollectionInterface

Returns a new collection of values re-indexed with consecutive integers,
discarding the original keys:

```php
$collection = new Collection(['a' => 1, 'b' => 2, 'c' => 3]);
$values = $collection->values()->toList(); // [1, 2, 3]
```

## String Operations

### implode()

`method` Cake\\Collection\\Collection::**implode**(string $glue, callable|string|null $path = null): string

Concatenates all elements in the collection into a string, separated by
the given glue string. Optionally extract values using a path before joining:

```php
$collection = new Collection(['a', 'b', 'c']);
echo $collection->implode(', '); // 'a, b, c'

// With path extraction
$collection = new Collection([
['name' => 'Alice'],
['name' => 'Bob'],
['name' => 'Charlie'],
]);
echo $collection->implode(', ', 'name'); // 'Alice, Bob, Charlie'

// With a callback
$collection = new Collection([1, 2, 3]);
echo $collection->implode(' + ', fn($n) => $n * 2); // '2 + 4 + 6'
```

## Conditional Operations

### when()

`method` Cake\\Collection\\Collection::**when**(mixed $condition, callable $callback): CollectionInterface

Conditionally applies a callback to the collection when the condition is truthy.
This enables fluent conditional chaining without breaking the method chain:

```php
$collection = new Collection($items)
->when($shouldFilter, function ($collection) {
return $collection->filter(fn($item) => $item['active']);
})
->when($sortByName, function ($collection) {
return $collection->sortBy('name');
});
```

If the condition is falsy, the collection is returned unchanged.

### unless()

`method` Cake\\Collection\\Collection::**unless**(mixed $condition, callable $callback): CollectionInterface

The inverse of `when()`. Conditionally applies a callback to the collection
when the condition is falsy:

```php
$collection = new Collection($items)
->unless($hasDefaults, function ($collection) use ($defaults) {
return $collection->append($defaults);
});
```

If the condition is truthy, the collection is returned unchanged.
Loading