PHP Exceptions allow you to replace generic errors with more meaningful error messages to your users and process the errors in a custom way. This tutorial will show a quick example from a Laravel package.
Custom exceptions are a great source of information for framework, package, tool, or module developers.
We're talking about spatie/laravel-permission package - look at the file that uses four custom Exception classes inside.
use Spatie\Permission\Exceptions\GuardDoesNotMatch;use Spatie\Permission\Exceptions\PermissionDoesNotExist; use Spatie\Permission\Exceptions\WildcardPermissionInvalidArgument;use Spatie\Permission\Exceptions\WildcardPermissionNotImplementsContract;use Spatie\Permission\Guard; // ... public function filterPermission($permission, $guardName = null){ // ... if (! $permission instanceof Permission) { throw new PermissionDoesNotExist(); } return $permission;}
If the permission doesn't exist in the database, the package throws an Exception. But how is it used, then?
Processing Exceptions
Here's the Exception class itself:
namespace Spatie\Permission\Exceptions; use InvalidArgumentException; class PermissionDoesNotExist extends InvalidArgumentException{ public static function create(string $permissionName, ?string $guardName) { return new static("There is no permission named `{$permissionName}` for guard `{$guardName}`."); } /** * @param int|string $permissionId * @return static */ public static function withId($permissionId, ?string $guardName) { return new static("There is no [permission] with ID `{$permissionId}` for guard `{$guardName}`."); }}
This will immediately tell our end developer that no such permission exists in the database. Now, let's look at a few usages in the Model:
Model
class Permission extends Model implements PermissionContract{ // ... public static function findByName(string $name, string $guardName = null): PermissionContract { $guardName = $guardName ?? Guard::getDefaultName(static::class); $permission = static::getPermission(['name' => $name, 'guard_name' => $guardName]); if (! $permission) { throw PermissionDoesNotExist::create($name, $guardName); } return $permission; } public static function findById(int|string $id, string $guardName = null): PermissionContract { $guardName = $guardName ?? Guard::getDefaultName(static::class); $permission = static::getPermission([(new static())->getKeyName() => $id, 'guard_name' => $guardName]); if (! $permission) { throw PermissionDoesNotExist::withId($id, $guardName); } return $permission; }}
We have two methods here: findByName()
and findById()
. Both of them will throw an Exception if permission is not found, but they will also provide a custom message that will tell us if that issue is with ID or name.
This is an excellent example of a package developer thinking about the end developer and providing a great experience with custom exceptions and error messages.
We can then catch this Exception individually and make our system react in a specific way if this happens.
You can learn more about exceptions and how to use them in our Handling Exceptions and Errors in Laravel course.
No comments or questions yet...