The Sprinkle Recipe dictates how your sprinkle is built, like a blueprint. UserFrosting services and framework will use the information from the recipe to initiate some services, and expose classes your sprinkle provides for other servicea to use.
Each sprinkle must have a recipe. It's not possible for a sprinkle to exist without a recipe, as it won't be possible to expose its class and service to the framework. It's possible however to customize other sprinkles, as we'll see later on this page.
SprinkleRecipe
InterfaceThe Sprinkle Recipe is a simple PHP class that provides standard methods which will be called by services to retrieve information about your sprinkle structure and the class it's registering. Every sprinkle recipe MUST implement the UserFrosting\Sprinkle\SprinkleRecipe
interface. If you started from the Skeleton, you already have a basic recipe.
This interface requires you to implement the following method in your recipe:
getName
: Returns the name of the sprinkle.getPath
: Returns the path of the sprinkle. getSprinkles
: Returns an array of dependent sub-sprinkles recipe. getRoutes
: Return an array of routes classes.getServices
: Return an array of services classes.SprinkleRecipe
interface, all of those methods are mandatory. Failure to implement the interface will result in an exception being thrown. However, it doesn't mean a method must return data. It's perfectly fine for a method to return an empty string or empty array.This method returns the name identifier of the sprinkle. This name is mostly used in debug interfaces to identify resources and classes registered by the sprinkle.
The method should return a string. For example:
public function getName(): string
{
return 'My Application';
}
This method returns the path of the sprinkle. This path should point where the src/
, assets/
, etc. folder is located, typically app/
. For example, if your recipe is in app/src/YourSprinkle.php
and your sprinkle structure looks like this...
├── app/
├── assets/
├── logs/
├── [...]
├── src/
├── [...]
└── YourSprinkle.php
└── [...]
├── public/
├── vendor/
├── composer.json
├── package.json
└── webpack.config.js
...getPath()
should point to /app
, or in this case the parent directory of where the recipe file is located :
public function getPath(): string
{
return __DIR__ . '/../';
}
app/
can actually be named whatever you want. As long as the recipe point to the folder containing all the sprinkle static resources.This methods returns the sub-sprinkles your sprinkle depends on. This makes it easier to integrate other sprinkles' classes and resources into your app without having to copy everything inside your own recipe.
The order the sprinkles are loaded is important. Files in one sprinkle may override files with the same name and path in previously loaded sprinkles. For example, if we created site/templates/pages/about.html.twig
, this would override core/templates/pages/about.html.twig
because we load the site
sprinkle after the core
sprinkle.
Sprinkles will be loaded in the order you list them (top one first), but also based on their respective dependencies. For example:
public function getSprinkles(): array
{
return [
Core::class,
Account::class,
Admin::class,
AdminLTE::class,
];
}
Since Admin
depends on Core
, Account
and AdminLTE
, it's not mandatory to relist them in your recipe. In fact, the code above is equivalent to this, since the other one will be registered by Admin
:
public function getSprinkles(): array
{
return [
Admin::class,
];
}
This also mean removing AdminLTE
as a dependency for example cannot be done by simply removing it from your recipe! It's impossible to remove AdminLTE
without removing Admin
, since Admin
cannot work without its dependency.
However, it also means the next example is also equivalent:
public function getSprinkles(): array
{
return [
AdminLTE::class,
Admin::class,
Account::class,
Core::class,
];
}
Let's look at the process for the above code :
Because of sprinkle dependencies, in all three examples the order will be Core -> Account -> AdminLTE -> Admin -> YOUR APP
.
php bakery sprinkle:list
command. The registered sprinkles will be displayed in the order they are registered.Return an array of routes classes. More details about this will be explored in Chapter 8 - Routes and Controllers.
For example, to register MyRoutes
class:
public function getRoutes(): array
{
return [
MyRoutes::class,
];
}
Return an array of services definitions. These will be explored in Chapter 7 - Dependency Injection
Example:
public function getServices(): array
{
return [
AlertStreamService::class,
CacheService::class,
];
}
Since your sprinkle is the last loaded sprinkle, it becomes the main sprinkle. This is important, as the main sprinkle is the entry point to the app. The main sprinkle class must be referenced in two entry files : /public/index.php
(web app/page entry) and /bakery
(CLI App).
For example, if your main sprinkle class fully qualified name is UserFrosting\App\MyApp
:
/public/index.php
// [...]
use UserFrosting\App\MyApp; // <--- Import here
use UserFrosting\UserFrosting;
$uf = new UserFrosting(MyApp::class); // <-- Reference here
$uf->run();
/bakery
// [...]
use UserFrosting\App\MyApp; // <--- Import here
use UserFrosting\Bakery\Bakery;
$bakery = new Bakery(MyApp::class); // <-- Reference here
$bakery->run();
The sprinkle recipe power comes from its modularity. To avoid having one huge recipe with empty content, optional features can be added only when necessary.
The available sub-recipes includes:
Recipe | Features |
---|---|
BakeryRecipe | Registering Bakery commands |
MigrationRecipe | Registering Migrations |
SeedRecipe | Registering Seeds |
MiddlewareRecipe | Registering Middlewares |
EventListenerRecipe | Registering Event Listeners |
TwigExtensionRecipe | Registering Twig Extension |
Your recipe simply needs to implement the corresponding interface. Classes may implement more than one interface if desired by separating each interface with a comma. For example :
class MyApp implements
SprinkleRecipe,
TwigExtensionRecipe,
MigrationRecipe,
EventListenerRecipe,
MiddlewareRecipe,
BakeryRecipe
{
Interface : UserFrosting\Sprinkle\BakeryRecipe
Methods to implements :
getBakeryCommands
: Return a list of Bakery commands classes
Example:
public function getBakeryCommands(): array
{
return [
BakeCommand::class,
ClearCacheCommand::class,
];
}
Interface : UserFrosting\Sprinkle\Core\Sprinkle\Recipe\MigrationRecipe
Methods to implement :
getMigrations
: Return a list of Migrations classes
Example:
public function getMigrations(): array
{
return [
SessionsTable::class,
ThrottlesTable::class,
];
}
Interface : UserFrosting\Sprinkle\Core\Sprinkle\Recipe\SeedRecipe
Methods to implement :
getSeeds
: Return a list of Seeds classes
Example:
public function getSeeds(): array
{
return [
DefaultGroups::class,
DefaultPermissions::class,
DefaultRoles::class,
];
}
Interface : UserFrosting\Sprinkle\MiddlewareRecipe
Methods to implement :
getMiddlewares
: Return a list of Middlewares classes
Example:
public function getMiddlewares(): array
{
return [
CsrfGuardMiddleware::class,
SessionMiddleware::class,
];
}
Interface : UserFrosting\Event\EventListenerRecipe
Methods to implement :
getEventListeners
: Allows to register Event Listeners
Example:
public function getEventListeners(): array
{
return [
AppInitiatedEvent::class => [
RegisterShutdownHandler::class,
ModelInitiated::class,
SetRouteCaching::class,
],
BakeryInitiatedEvent::class => [
ModelInitiated::class,
SetRouteCaching::class,
],
ResourceLocatorInitiatedEvent::class => [
ResourceLocatorInitiated::class,
],
];
}
Interface : UserFrosting\Sprinkle\Core\Sprinkle\Recipe\TwigExtensionRecipe
Methods to implement :
getTwigExtensions
: Return a list of Twig Extension classes
Example:
public function getTwigExtensions(): array
{
return [
CoreExtension::class,
AlertsExtension::class,
];
}
A default install, from the Skeleton, enables every default sprinkle. But your app may not require every feature provided by these default sprinkles. For example, you might not need the Admin sprinkle if you don't need any user management features.
In this case, two files need to be edited : composer.json
and the Sprinkle Recipe.
In /composer.json, remove the sprinkle from the Composer requirements :
"userfrosting/sprinkle-admin": "^5.1",
Since changes were made to composer.json, composer need to be updated (composer update
).
In the Sprinkle Recipe, Admin:class
can be removed from the getSprinkles()
method:
public function getSprinkles(): array
{
return [
Core::class,
Account::class,
//Admin::class,
AdminLTE::class,
];
}
Sometimes you may want to customize one of the dependent sprinkles. For example, you may want to remove all routes defined in the Account sprinkle. Or use only one migrations from the AwesomeStuff
sprinkle. There's two easy way to customize dependent sprinkles, either by cherry-picking resources or extending the dependent sprinkle's recipe.
This method is best when you want a small number of resources from a dependent sprinkle. For example, when you want one migration from the AwesomeStuff
sprinkle. The drawback is if the dependent sprinkle is updated, you may need to manually update your code. If you want to import many resources (but not all of them) from a dependent sprinkle, it's best to use the other method.
In this case, instead of adding the dependent sprinkle (in getSprinkles
), you open the dependent sprinkle recipe and copy the code you want into your recipe.
This method is best used when you want to remove a small number of resources from a dependent sprinkle. As with the previous method, if the dependent sprinkle is updated, you may need to manually update your code. If you want to only one resource from a dependent sprinkle, it's best to use the previous method to import one, than to remove everything else.
For example, you may want to remove all routes defined in the Account sprinkle :
namespace UserFrosting\App;
use UserFrosting\Sprinkle\Account\Account;
/**
* Overwrite main Account Sprinkle Class, to remove routes.
*/
class CustomAccount extends Account
{
/**
* {@inheritDoc}
*/
public function getRoutes(): array
{
return [];
}
}
In this case, instead of depending on Account
in getSprinkles
, you'll add CustomAccount
in your sprinkle getSprinkles
. All other methods from Account
will be included via CustomAccount
.
You'll then have two recipes in your sprinkle, e.g.: MyApp
and CustomAccount
, side by side. MyApp
will still be main sprinkle, referenced in index.php
and bakery
, since CustomAccount
is a dependency of MyApp
.