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
4 changes: 3 additions & 1 deletion docs/14-upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Aside from "@components/Aside.astro"
Some plugins you're using may not be available in v5 just yet. You could temporarily remove them from your `composer.json` file until they've been upgraded, replace them with a similar plugins that are v5-compatible, wait for the plugins to be upgraded before upgrading your app, or even write PRs to help the authors upgrade them.
</Aside>

You can upgrade your Filament app by running the automated upgrade script. This script will check your codebase for compatibility issues, and suggest Composer update commands to run:
The first step to upgrade your Filament app is to run the automated upgrade script. This script will automatically upgrade your application to the latest version of Filament and make changes to your code, which handles breaking changes:

```bash
composer require filament/upgrade:"^5.0" -W --dev
Expand All @@ -46,6 +46,8 @@ composer update
```
</Aside>

Make sure to carefully follow the instructions, and review the changes made by the script. You may need to make some manual changes to your code afterwards, but the script should handle most of the repetitive work for you.

You can now `composer remove filament/upgrade --dev` as you don't need it anymore.

## Upgrading Livewire
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\Access\Response;
use Illuminate\Database\Eloquent\Model;
use UnitEnum;

use function Filament\get_authorization_response;

Expand All @@ -19,7 +20,7 @@ public static function canAccess(): bool
return static::canViewAny();
}

public static function getAuthorizationResponse(string $action, ?Model $record = null): Response
public static function getAuthorizationResponse(string | UnitEnum $action, ?Model $record = null): Response
{
if (static::shouldSkipAuthorization()) {
return Response::allow();
Expand All @@ -28,15 +29,15 @@ public static function getAuthorizationResponse(string $action, ?Model $record =
return get_authorization_response($action, $record ?? static::getModel(), static::shouldCheckPolicyExistence());
}

public static function can(string $action, ?Model $record = null): bool
public static function can(string | UnitEnum $action, ?Model $record = null): bool
{
return static::getAuthorizationResponse($action, $record)->allowed();
}

/**
* @throws AuthorizationException
*/
public static function authorize(string $action, ?Model $record = null): ?Response
public static function authorize(string | UnitEnum $action, ?Model $record = null): ?Response
{
return static::getAuthorizationResponse($action, $record)->authorize();
}
Expand Down
55 changes: 55 additions & 0 deletions packages/upgrade/bin/filament-v5
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,24 @@ render(<<<'HTML'

<br />

This script will attempt to handle breaking changes for you, and will guide you through any manual steps that may be required.

<br />

If you have any questions, please reach out to us on <a href="https://filamentphp.com/discord" class="underline font-bold">Discord</a> or <a href="https://github.com/filamentphp/filament/discussions/new?category=q-a" class="underline font-bold">GitHub</a>.
</div>

<div>
To begin, please ensure that you are using a version control system such as Git.

<br />

We will make changes directly to your files, and you will need to be able to revert them if something goes wrong.

<br />

<strong>Please commit any changes you have made to your project before continuing.</strong>
</div>
</div>

<br />
Expand All @@ -27,6 +43,45 @@ HTML);

require __DIR__ . '/../src/check-compatibility.php';

$directories = $argv[1] ?? ask(<<<HTML
<div>
Please provide a comma-separated list of directories containing Filament code to upgrade (e.g. <strong>app</strong>, <strong>app-modules</strong>, <strong>src</strong>).

<br />

<span class="bg-amber-600 text-amber-50 mr-1">
You can skip this if you have a normal Laravel app structure:
</span>
</div>
HTML) ?: 'app';

render(<<<HTML
<p class="bg-green-600 text-green-50">
Starting upgrade...
</p>
HTML);

$rectorScriptPath = implode(DIRECTORY_SEPARATOR, ['vendor', 'bin', 'rector']);

foreach (explode(',', $directories) as $directory) {
$directory = trim($directory);
$directory = trim($directory, DIRECTORY_SEPARATOR);

render(<<<HTML
<p>
Start processing <strong>/{$directory}</strong> to fix code affected by breaking changes.
</p>
HTML);

exec("{$rectorScriptPath} process {$directory} --config vendor/filament/upgrade/src/rector.php --clear-cache");

render(<<<HTML
<p class="pt-2">
Finished processing <strong>/{$directory}</strong>.
</p>
HTML);
}

$requireCommands = [];

foreach (json_decode(file_get_contents('composer.json'), true)['require'] as $package => $version) {
Expand Down
3 changes: 2 additions & 1 deletion packages/upgrade/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"require": {
"php": "^8.2",
"nunomaduro/termwind": "^2.0"
"nunomaduro/termwind": "^2.0",
"rector/rector": "^2.0"
},
"autoload": {
"psr-4": {
Expand Down
116 changes: 116 additions & 0 deletions packages/upgrade/src/Rector/SimpleMethodChangesRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Filament\Upgrade\Rector;

use Closure;
use Filament\Resources\Resource;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\UnionType;
use PHPStan\Type\ObjectType;
use Rector\Rector\AbstractRector;

class SimpleMethodChangesRector extends AbstractRector
{
/**
* @return array<array{
* class: class-string | array<class-string>,
* changes: array<string, Closure>,
* }>
*/
public function getChanges(): array
{
$addUnitEnumToAuthorizationActionParamModifier = static function (ClassMethod $node): void {
foreach ($node->getParams() as $param) {
if ($param->var->name !== 'action') {
continue;
}

$param->type = new UnionType([new Identifier('string'), new FullyQualified('UnitEnum')]);
}
};

return [
[
'class' => [
Resource::class,
],
'changes' => [
'getAuthorizationResponse' => $addUnitEnumToAuthorizationActionParamModifier,
'can' => $addUnitEnumToAuthorizationActionParamModifier,
'authorize' => $addUnitEnumToAuthorizationActionParamModifier,
],
],
];
}

public function getNodeTypes(): array
{
return [Class_::class, Enum_::class];
}

/**
* @param Class_ | Enum_ $node
*/
public function refactor(Node $node): ?Node
{
$touched = false;

foreach ($this->getChanges() as $change) {
if (! $this->isClassMatchingChange($node, $change)) {
continue;
}

foreach ($change['changes'] as $methodName => $modifier) {
foreach ($node->getMethods() as $method) {
if (! $this->isName($method, $methodName)) {
continue;
}

$modifier($method);

$touched = true;
}
}
}

return $touched ? $node : null;
}

/**
* @param array{
* class: class-string | array<class-string>,
* } $change
*/
public function isClassMatchingChange(Class_ | Enum_ $class, array $change): bool
{
$classes = is_array($change['class']) ?
$change['class'] :
[$change['class']];

$classes = array_map(fn (string $class): string => ltrim($class, '\\'), $classes);

foreach ($classes as $classToCheck) {
if ($class instanceof Enum_) {
foreach ($class->implements as $enumInterface) {
if ($enumInterface->toString() === $classToCheck) {
return true;
}
}

continue;
}

if ($this->isObjectType($class, new ObjectType($classToCheck))) {
return true;
}
}

return false;
}
}
10 changes: 10 additions & 0 deletions packages/upgrade/src/rector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

use Filament\Upgrade\Rector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([
Rector\SimpleMethodChangesRector::class,
]);
};
Loading