Upgrade Guide
Apiato 13.x is a major release that introduces breaking changes and new features. This release is compatible with Laravel 11.x and 12.x.
Upgrade Path
- Upgrade to Laravel 11.x from 10.x.
- Upgrade to Apiato and Core 13.0 from 12.x and 8.x respectively.
- Upgrade to Laravel 12.x from 11.x.
Upgrading Laravel to 11.0 from 10.x
Follow the official Laravel 11 upgrade guide to update your codebase accordingly.
Upgrading Laravel to 12.0 from 11.x
Follow the official Laravel 12 upgrade guide to update your codebase accordingly.
Update the apiato/core package to version 13.1,
as this is the only compatible version with Laravel 12.x.
The most significant change involves the laravel/passport package.
Follow the official Laravel Passport 13 upgrade guide
to update your codebase accordingly.
You can review the Laravel 12.x support pull request
to help you with the upgrade process and identify any other modifications required to adapt your codebase.
Upgrading Apiato to 13.0 from 12.x and Core to 13.0 from 8.x
This guide will walk you through upgrading Apiato and your project to the latest versions.
Upgrade Utilities
Legacy Bridge
The Legacy Bridge is a set of utilities to help you gradually upgrade your codebase.
Rector Rules
Apiato provides a set of custom Rector rules to help automate some aspects of the upgrade process.
Learn more about Rector
Updating Dependencies
PHP 8.2.0 Required
PHP 8.2.0 or greater is now required.
Composer Dependencies
Perform these steps to update your dependencies.
Remove First-Party Apiato Packages
Delete all Apiato first-party packages in app/Containers/Vendor to avoid dependency conflicts.
These packages will be reinstalled after running composer update.
Consolidate Dependencies
If the following dependencies are defined in the Ship or any Container composer.json file,
move them to your application’s root composer.json file:
apiato/corelaravel/passportspatie/laravel-permission
Update Dependency Versions
Update the following dependencies in your composer.json files:
require
apiato/coreto^13.0laravel/passportto^12.0spatie/laravel-permissionto^6.0apiato/documentation-generator-containerto^4.0
require-dev
nunomaduro/collisionto^8.1phpunit/phpunitto^11.0barryvdh/laravel-ide-helperto^3.0vimeo/psalmto^6.0Don’t forget to also updatepsalmplugins if you have them:"psalm/plugin-laravel": "^3.0",
"psalm/plugin-mockery": "^1.2",
"psalm/plugin-phpunit": "^0.19.2",
Remove Unnecessary Dependencies
Remove these dependencies from your root composer.json file:
spatie/laravel-ignition (Laravel now provides its own error page)
Following dependencies are also no longer required as they are now either managed by apiato/core or Laravel Framework itself.
PHP extensions
ext-curlext-gettextext-mbstringext-opensslext-pdoext-sodiumext-tokenizer
Packages
guzzlehttp/guzzlelaravel/frameworklaravel/tinker
Review Package Upgrade Guides
Check the upgrade guides for each dependency to be aware of additional breaking changes:
Remove the existing vendor directory for a clean start:
rm -rf vendor
Pull in the latest dependencies:
composer update
Because of the merging behavior of wikimedia/composer-merge-plugin,
run the command a second time to ensure that all dependencies are correctly installed:
composer update
Abstract Namespace
The Core abstract classes have been relocated from Apiato\Core\Abstracts to Apiato\Core.
Update your codebase to use this new namespace.
App Configuration
Laravel 11 and Apiato 13 configurations are simplified, and both now provide a fluid API for configuration.
Replace the contents of bootstrap/app.php file with the following as a base, and customize it as needed:
<?php
use Apiato\Foundation\Apiato;
use Apiato\Http\Middleware\ProcessETag;
use Apiato\Http\Middleware\ValidateJsonContent;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
$basePath = dirname(__DIR__);
$apiato = Apiato::configure(basePath: $basePath)->create();
return Application::configure(basePath: $basePath)
->withProviders($apiato->providers())
->withEvents($apiato->events())
->withRouting(
web: $apiato->webRoutes(),
channels: __DIR__ . '/../app/Ship/Broadcasts/channels.php',
health: '/up',
then: static fn () => $apiato->registerApiRoutes(),
)
->withMiddleware(function (Middleware $middleware) {
// Previously applied via app/Ship/Kernels/HttpKernel class
$middleware->api(append: [
ValidateJsonContent::class,
ProcessETag::class,
]);
// Uncomment and adjust these if using web routes:
// $middleware->redirectUsersTo(fn ($request): string => action(HomePageController::class));
// $middleware->redirectGuestsTo(fn ($request): string => action([LoginController::class, 'showForm']));
})
->withCommands($apiato->commands())
->withExceptions(static function (Exceptions $exceptions) {})
->create();
Move any custom middleware registrations from app/Ship/Kernels/HttpKernel.php to the bootstrap/app.php file.
Move any custom configurations from app/Ship/Kernels/ConsoleKernel.php and App\Ship\Kernels\HttpKernel to the bootstrap/app.php file.
Remove App\Ship\Kernels\HttpKernel and App\Ship\Kernels\ConsoleKernel classes.
Testing
TestCase Class
The Apiato\Core\Abstracts\Tests\PhpUnit\TestCase class namespace has changed to Apiato\Core\Testing\TestCase.
LazilyRefreshDatabase Trait
The Illuminate\Foundation\Testing\LazilyRefreshDatabase trait has been removed from the Core TestCase.
Add LazilyRefreshDatabase to your App\Ship\Parents\Tests\TestCase if you want this functionality.
Removed Methods
The testing utilities provided by Apiato have been simplified to embrace Laravel’s testing approach.
The following methods are no longer available:
getTestingUsergetTestingUserWithoutAccessmakeCallinjectIdendpointgetAuthsetResponseObjectAndContentgetResponseContentArraygetResponseContentsetResponseContentgetResponseContentObjectauthurl- etc...
For a gradual upgrade in large codebases, refer to the Apiato Legacy Bridge.
HasResourceKeyTrait has been removed from the Core Value Object (Apiato\Core\Values\Value).
If your Value Objects are relying on the HasResourceKeyTrait,
you need to refactor them to use the new HasResourceKey trait
and implement the ResourceKeyAware interface.
Service Providers
The Apiato\Core\Abstracts\Providers\MainServiceProvider class has been removed.
Existing service providers extending this class should be refactored
to extend Apiato\Core\Providers\ServiceProvider class instead.
e.g, App\Ship\Parents\Providers\ServiceProvider class.
Automatic Registration
All providers within the Providers directories of Containers and Ship are now automatically registered.
There is no longer a requirement to have a specifically named MainServiceProvider service provider per container.
Any parent providers under app/Ship/Parents/Providers will NOT be registered automatically.
If you need to do something in a provider’s register or boot methods,
extend those providers in a Container or Ship Providers directory.
Specific Provider Changes
AuthServiceProvider
The Apiato\Core\Abstracts\Providers\AuthServiceProvider class has been removed.
- If you were manually registering policies outside Laravel’s policy discovery, follow the Laravel manual registration guide.
- Refactor all service providers extending the following service providers to extend
App\Ship\Parents\Providers\ServiceProvider:App\Ship\Parents\Providers\AuthServiceProviderApiato\Core\Abstracts\Providers\AuthServiceProviderIlluminate\Foundation\Support\Providers\AuthServiceProvider
- Move any custom registrations from
App\Ship\Parents\Providers\AuthServiceProviderto another service provider and then remove it.
MiddlewareServiceProvider
The Apiato\Core\Abstracts\Providers\MiddlewareServiceProvider class has been removed.
Migrate your middleware configurations
to the bootstrap/app.php file as per Laravel’s middleware registration documentation.
Then, remove your old MiddlewareServiceProvider classes.
Let's
say you have customized Laravel's Illuminate\Auth\Middleware\RedirectIfAuthenticated middleware
and registered it in your MiddlewareServiceProvider:
class MiddlewareServiceProvider extends ParentMiddlewareServiceProvider
{
protected array $middlewareAliases = [
'guest' => RedirectIfAuthenticated::class,
];
}
You can now configure it in bootstrap/app.php like this:
return Application::configure(basePath: $basePath)
...
->withMiddleware(function (Middleware $middleware) {
$middleware->api(
replace: [
'guest' => RedirectIfAuthenticated::class,
]
);
...
->create();
BroadcastServiceProvider
- Removed:
Apiato\Core\Abstracts\Providers\BroadcastServiceProvider - Action:
Remove your custom
App\Ship\Parents\Providers\BroadcastServiceProviderand configure channels inbootstrap/app.phpas shown above.
Parent Method Calls
You no longer need to call parent::register() or parent::boot() in your service providers.
All service providers’ register and boot methods are now invoked independently.
Removed Properties
The $serviceProviders and $aliases properties have been removed.
Refactor your service providers to register or alias dependencies within the register and boot methods.
// before
public array $serviceProviders = [
ThirdPartyServiceProvider::class,
];
public array $aliases = [
'something' => ThirdPartyServiceProvider::class,
];
// after
public function register(): void
{
// Reminder: providers under Containers or Ship are autoloaded
// and don't need to be registered manually.
$this->app->register(ThirdPartyServiceProvider::class);
$this->app->alias(ThirdPartyServiceProvider::class, 'something');
}
Middleware
The following classes have been moved or renamed:
| From | To |
|---|---|
Apiato\Core\Abstracts\Middlewares | Apiato\Core\Middleware |
Apiato\Core\Abstracts\Middlewares\Http\ProcessETagHeadersMiddleware | Apiato\Http\Middleware\ProcessETag |
Apiato\Core\Abstracts\Middlewares\Http\ValidateJsonContent | Apiato\Http\Middleware\ValidateJsonContent |
The Apiato\Core\Abstracts\Middlewares\Http\ProfilerMiddleware class has been removed.
The Apiato\Core\Middleware\Http\Authenticate class has been removed.
Use Illuminate\Auth\Middleware\Authenticate instead.
Custom middleware like ProcessETag and ValidateJsonContent must now be registered in the bootstrap/app.php file.
Take a look at the App Configuration section for details.
Resource Key
If you have custom models that don't extend the App\Ship\Parents\Models models,
you need to implement the Apiato\Http\Resources\ResourceKeyAware interface
and use the Apiato\Http\Resources\HasResourceKey trait.
e.g., App\Containers\AppSection\Authorization\Models\Roles.
HasResourceKey Interface
The Apiato\Core\Contracts\HasResourceKey interface has been renamed
and moved to Apiato\Http\Resources\ResourceKeyAware.
HasResourceKey Trait
The Apiato\Core\Traits\HasResourceKeyTrait trait has been renamed
and moved to Apiato\Http\Resources\HasResourceKey.
ModelTrait
The Apiato\Core\Abstracts\Traits\ModelTrait trait has been renamed
and moved to Apiato\Core\Models\InteractsWithApiato.
Value Objects
Apiato\Core\Values\Value abstract class is now declared as readonly.
Add the readonly keyword to your parent Value Object class and all Value Objects that extend it.
Models
Models $resourceKey property is no longer used.
If you were customizing the resource key in your models, you can now do so by overriding the getResourceKey method.
Laravel Passport
Migration Adjustments
Passport 12.0 no longer loads its own migrations automatically.
Copy Passport migrations from the Apiato 13.x branch into your application
(create the Data/Migrations directory if it doesn’t exist).
Password Grant
As of Passport 12.0, the password grant is disabled by default.
Enable it by calling enablePasswordGrant in the boot method of one of your service providers:
public function boot(): void
{
Passport::enablePasswordGrant();
}
Deferrable Provider
Remove the Illuminate\Contracts\Support\DeferrableProvider from the service provider that configures Passport.
For example
// before
use App\Ship\Parents\Providers\ServiceProvider as ParentAuthServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ParentAuthServiceProvider implements DeferrableProvider
{
public function boot(): void
{
Passport::enablePasswordGrant();
}
}
// after
use App\Ship\Parents\Providers\ServiceProvider as ParentAuthServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ParentAuthServiceProvider
{
public function boot(): void
{
Passport::enablePasswordGrant();
}
}
Carbon 3
In Carbon v3, PHPDoc annotations have been converted to real types. Methods that previously accepted different argument types may now throw a TypeError.
For instance, this example will now throw an error if config('apiato.api.expires-in') returns a string:
Passport::tokensExpireIn(Carbon::now()->addMinutes(config('apiato.api.expires-in')));
To fix it, explicitly cast the string to an integer:
Passport::tokensExpireIn(Carbon::now()->addMinutes((int)config('apiato.api.expires-in')));
Review Carbon’s release notes and documentation for more details.
Request
Removed Methods
The following methods have been removed from requests:
injectDatawithUrlParametersgetAccessArraygetDecodeArraygetUrlParametersArrayhasAccesshasAnyPermissionAccesshasAnyRoleAccesscheckmapInputgetInputByKey
For a gradual upgrade in large codebases, refer to the Apiato Legacy Bridge.
Authorization
Custom authorization features have been removed and $access property is no longer used.
-
Use Laravel Policies for authorization.
-
Remove the
$accessproperty from your request classes. -
hasAccess -
hasAnyPermissionAccess -
hasAnyRoleAccess -
check
Example Conversion:
// before
use App\Ship\Parents\Requests\Request as ParentRequest;
final class DeleteUserRequest extends ParentRequest
{
protected array $access = [
'permissions' => 'delete',
'roles' => '',
];
public function authorize(): bool
{
return $this->check(['hasAccess']);
}
}
// after
use App\Containers\AppSection\User\Models\User;
use App\Ship\Parents\Requests\Request as ParentRequest;
final class DeleteUserRequest extends ParentRequest
{
public function authorize(): bool
{
return $this->user()->can('delete', [User::class]);
}
}
URL Parameter Validation
The $urlParameters property and the getUrlParametersArray method have been removed.
URL parameters are no longer available for validation in the rules method.
- Remove the validations that are based on the URL parameters.
- Remove any reference to the
$urlParametersproperty. - Remove calls to
getUrlParametersArray.
Note that invalid URL parameters now result in a 404 error rather than a 422 error.
Hashed ID Decoding
In addition to the previous behavior, the $decode property can now decode route parameters.
The catch is that the decoded route parameters will not be available via input or all methods.
Instead, you can access them via route method.
// endpoint
/users/{id}
class DemoRequest extends ParentRequest
{
protected array $decode = ['id'];
}
$id = $request->route('id');
Input Sanitization
sanitizeInput has been renamed to sanitize.
Error Handling
All Apiato custom error-handling logics has been removed.
Removed Methods on Exceptions
debugwithErrorsgetErrors
ExceptionsHandler
The custom App\Ship\Exceptions\Handlers\ExceptionsHandler is no longer used.
Remove this class and configure exception handling in bootstrap/app.php file.
HTTP vs. Non-HTTP Exceptions
Exceptions are now divided into two groups: HTTP Exceptions and Non-HTTP Exceptions.
Create a parent HTTP exception class in app/Ship/Parents/Exceptions/HttpException.php:
namespace App\Ship\Parents\Exceptions;
use Apiato\Core\Exceptions\HttpException as AbstractException;
abstract class HttpException extends AbstractException {}
You now have two parent classes: Exception (non-HTTP) and HttpException (HTTP).
Update your custom exceptions:
- Extend
App\Ship\Parents\Exceptions\HttpExceptionfor HTTP errors. - Extend
App\Ship\Parents\Exceptions\Exceptionfor non-HTTP errors.
Exception Instantiation
Exceptions now require mandatory constructor parameters.
For example, calling new NotFoundHttpException() with no arguments no longer works.
You have to pass the $message and the $code arguments.
Refactor your exception instantiations accordingly.
See Rector Rules for automated refactoring utilities.
Removed Core Exceptions
All exceptions under the Apiato\Core\Exceptions namespace have been removed.
Remove their usages from your application.
Apiato\Core\Exceptions\AuthenticationExceptionApiato\Core\Exceptions\GeneratorErrorExceptionApiato\Core\Exceptions\IncorrectIdExceptionApiato\Core\Exceptions\InvalidTransformerExceptionApiato\Core\Exceptions\MissingJSONHeaderExceptionApiato\Core\Exceptions\MissingTestEndpointExceptionApiato\Core\Exceptions\UndefinedMethodExceptionApiato\Core\Exceptions\UnsupportedFractalIncludeExceptionApiato\Core\Exceptions\WrongConfigurationsExceptionApiato\Core\Exceptions\WrongEndpointFormatException
Apiato now returns 401 instead of 403 when a user is unauthenticated (matching Laravel’s default behavior).
Response
Transformation
The transform method in API Controllers is removed.
Use the Response facade instead.
Update your app/Ship/Configs/fractal.php file to use the Apiato Response class
'fractal_class' => \Apiato\Http\Response::class
Replace all $this->transform calls with the Response facade.
// before
return $this->transform($data, Transformer::class);
// after
return Response::create($data, Transformer::class);
See Upgrade Utilities for automated refactoring utilities.
Filtering
The approach to filtering responses has changed. Check out its documentation and update your code accordingly.
Repository
Some repository methods are strongly typed or behave differently.
boot Method
boot now has a void return type.
If you have overridden the boot method, adjust your method signature accordingly.
getById Method
getById has been renamed to findOrFail.
delete Method
delete method now returns bool or throws an exception.
If you have overridden the delete method, adjust your method signature accordingly.
create Method
create now throws Apiato\Core\Repositories\Exceptions\ResourceCreationFailed instead of \Exception.
update Method
update now throws Apiato\Core\Repositories\Exceptions\ResourceNotFound instead of \Illuminate\Database\Eloquent\ModelNotFoundException.
find Method
find now returns null if no result is found (mirroring Laravel’s default find behavior). Previously, it might have thrown an exception.
To maintain the previous behavior of the find, create, and update methods,
use the Legacy Bridge.
Criteria
The Apiato\Core\Abstracts\Criterias class has been moved to Apiato\Core\Criteria.
Hash IDs
Apiato\Core\Traits\HashIdTrait is removed.
Previously, you could use $this->decode(...) and $this->encode(...) methods in the Controller and Request classes
to decode and encode hash IDs.
Now, you need to use the hashids helper function instead.
Use the hashids() helper function instead.
$this->decode(...)→hashids()->decode(...)$this->encode(...)→hashids()->encode(...)Hashids::decode(...)→hashids()->decode(...)Hashids::encode(...)→hashids()->encode(...)- etc...
decode method now returns an int or an array depending on the input.
Previously, it always returned an array.
Jobs
The Core Job class (Apiato\Core\Jobs\Job) no longer includes any traits or implements interfaces.
Add the necessary traits and interfaces in your Ship’s parent Job class. For example:
namespace App\Ship\Parents\Jobs;
use Apiato\Core\Jobs\Job as AbstractJob;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
abstract class Job extends AbstractJob implements ShouldQueue
{
use InteractsWithQueue;
use Queueable;
use SerializesModels;
}
Validation
The custom no_spaces validation rule has been removed.
Remove its usage or register it manually in the boot method of your service provider.
See the source code for reference.
Database Seeding
Apiato no longer uses Laravel’s database/seeders/DatabaseSeeder.php. Instead, place your seeders in the Ship or any Container’s database/seeders directory:
- Remove
database/seeders/DatabaseSeeder.phpif present. - Move your seeding logic into
ShiporContainerseeder classes.
The apiato:seed-deploy and apiato:seed-test commands have been moved from the Core to the Apiato.
So they will be only available in new Apiato projects.
To use them in your existing project, either:
- Create your own custom command to seed your database.
- Or copy the
apiato:seed-deployandapiato:seed-testcommands from the Apiato 13.x branch into your application.- Remember to also copy the
app/Ship/Seedersdirectory.
- Remember to also copy the
Commands
The Apiato\Core\Abstracts\Commands\ConsoleCommand class has been renamed
and moved to Apiato\Core\Console\Command.
Notifications
The notification.channels configuration has been removed.
You can no longer add a notification channel to all notifications
by adding it to the notification.channels array in the configuration file.
abstract class Notification extends LaravelNotification
{
// This method has been removed from the parent class
public function via($notifiable): array
{
return config('notification.channels');
}
}
If you depend on this feature, you can add the removed method to your parent notification class.
API URL Configuration
Path Prefix
The apiato.api.prefix configuration option has been removed.
You can now configure the path prefix using the Apiato configuration.
Version Prefix
The apiato.api.enable_version_prefix configuration option has been removed.
You can now configure the version prefix using the Apiato configuration.
Uncamelize Function
The uncamelize helper function has been removed.
Miscellaneous
We recommend reviewing changes in apiato/apiato and apiato/core repositories between versions 12.x and 13.x and 8.x and 13.x respectively, using GitHub’s comparison tools. Not all changes are mandatory. Some involve configuration or comments you may wish to sync.