Developers Guides - Package Development¶
So, you want to write a SeAT package? Hopefully this guide helps you along the way! This guide was written while writing the API package for SeAT here. I figured it would be best to try and capture the process to help in case I miss any important details.
Be sure to also have a look at the Development Tips page!
I think its important to keep in mind a few things about how SeAT is put together. The most important being a brief description of what each core package offers, and how you can integrate with them. For a breakdown on what the core packages provide, please refer the to [breakdown].
SeAT 4 is written on Laravel 6, while SeAT 5 uses Laravel 10. A very good thing to do would be to actually read the documentation top->bottom and get an idea of what is possible with the framework. SeAT core packages make heavy use of many of the features, based directly of what has been interpreted by this very documentation.
If you really want to start contributing packages, but just cant get your head around this whole Laravel thing, then I can suggest you have a look at this excellent free course material covering the basics of what you will encounter in the SeAT codebase. https://laracasts.com/series/laravel-6-from-scratch
Other plugins and the SeAT core are also a good learning resource.
The very first thing to do is prepare the empty git repository on say Github, as well as the composer package itself. Clone a clean repository, and run
composer init, answering any questions. Once that is done, edit the resultant
composer.json and prepare the
autoload block. SeAT core follows the PSR-4 autoloading standard. I will suggest you do the same. For some more detailed composer info, refer to the docs here. For the API package, I am going to autoload Seat\Api from the
src directory using PSR-4.
As mentioned in the package breakdowns, the eveseat/seat repository bootstraps packages via service providers or package discovery. This is actually a Laravel convention that SeAT just follows. To get our package ready, we need to create a service provider. For the API package, I create an
ApiServiceProvider class in
src/ directory which extends
Seat\Services\AbstractSeatPlugin. Our packages file structure now looks as follows:
AbstractSeatPlugin class is extending Laravel
ServiceProvider class and ship you with useful methods that help to register your plugin in the stack (including version discovery, permissions registration, etc...).
You must use it rather than the standard one, especially if you want permissions and get your packages in Settings > SeAT Settings > SeAT Module Versions pane.
From here you pretty much free to do what you want. How you structure the package will obviously depend on what exactly your package provides. In principle, I prefer to follow the same package structure as Laravel does for web / console features. Since we are going to be providing web features with the API, we will quickly create a few folders in preparation for this. I know beforehand that we are going to need a model to store API tokens; middleware to authenticate API requests; routes and controllers for the actual api logic (preferably making use of the eveapi/services repository classes for data access) as well as a few web views for administrators to generate API tokens for applications. With that in mind, the initial structure looks as follows:
This will obviously change as we progress building the package.
Routes and controllers¶
To start testing the API, we need to add a route and controller to process some requests and responses. My
routes.php file will have a global
Route::group() to encapsulate the routes in the Seat\Api namespace as well as prefix them with
See the final product here for a more complete example.
Next, I add some logic with a route to
/, update the base frameworks
composer.json to autoload the Seat\Api namespace from the directory where my package lives, run
composer dump-autoload and add the service provider to the eveseat/seat repositories
app.php providers array. Lastly, I add a method to the service provider to load the routes and call it form the
boot() method in the generated stub.
See the complete service provider here
As a final test, I check that my route is accessible from a booted SeAT app. :)
Obviously, some routes are not for everyone's eyes. SeAT comes with middleware that can be used to filter out requests that may not be authorized for your route. As can be seen in the example below (from here), we are filtering out requests to
api-admin for only superusers.
I suggest you have a look at the
eveseat/web packages routes definitions for more examples on how the middleware is used. https://github.com/eveseat/web/tree/master/src/Http/Routes
It is recommended that you scope the route name defined in
'as' => 'seatcore::api-admin.list'. For example all routes from the seat core start with
seatcore like this:
seatcore::my.route.to.someting. You should follow a similar format:
To auth our API requests, we are going to go with token based authentication for now. We want users to present us with a
X-Token header (from a valid allowed src IP address) before they may proceed with their request. To do this, we will filter requests using middleware. Thankfully, again, Laravel comes with a command to stub us some empty middleware. Run
php artisan make:middleware ApiToken and copy it to your projects
Middleware folder. Next we should register the middleware in our service provider. I do this by adding a method and calling it in
See the complete middleware here
Although almost all of our interfacing with this package will be via the json api endpoints, we need to add a few routes that will give an administrator the ability to generate API tokens as well as view logs etc. This will be for an administrator, so we will re-use the ACL features provided by the
eveseat/web package to ensure that only admins an access our api-admin routes. Views live in
resources/views and are bootstrapped to the
api namespace in the service provider. See the service provider here for an example.
Integrating with the sidebar is also really easy. All you have to do is create a config file, bootstrap it in the service provider and viola. The config file itself has a set structure for the
web package to interpret and can be seen here.
The format is generally an array, whereby the first key is the name of your package (
api in this case). Thereafter you can specify the main entry, and any sub entries you want to add. The
route key should refer to the named route. The sidebar loader will resolve the route itself for you. If you have any permissions requirements for your package, the
permission key can be used together with a required SeAT permission to render the view.
Integrating with the character sub menus is also really easy. Just like the sidebar, all you have to do is create a config file, bootstrap it in the service provider and viola. The namespace should be named
package.character.menu in your service provider. A sample config file can be seen below:
Integrating with the corporation sub menus is also really easy. Just like the sidebar and character menus, all you have to do is create a config file, bootstrap it in the service provider and viola. The namespace should be named
package.corporation.menu in your service provider. A sample config file can be seen below:
Bootstrapping menu items¶
In the above items, we refer to the files needing to be bootstrapped via the service provider. All this really means is that we have to tell the Laravel application where to find configuration information for a namespace. So, if we wanted to add a sidebar item, we would add the following line to the
register() method of the service provider:
The first argument is the file with the sidebar definitions, the second is the namespace.
You are able to register and use your own permissions for use within SeAT. This is relatively simple and done by creating a config file in the location
Config/Permissions/package.permissions.php. It should return an array of the following format:
|label||yes||The displayed name of your permission. It must be a translation token.|
|description||The displayed permission description. It should help user to determine what the permission is doing. It must be a translation token.|
|division||It will show a "category" icon to help user figures what will be impacted by the permission. Value can be one of
|gate||If you need to manage your permission with a custom policy, you can provide a policy FQCN.|
The definition key (
planetary in the upper sample) will be used as permission unique identifier by the system. This is the one stored in the database, together with scope.
By default, if no gate are provided, those shipped in core will be used according to this pattern :
- character scope: CharacterPolicy, this will require an instance of CharacterInfo to be used in your checks
- corporation scope: CorporationPolicy, this will require an instance of CorporationInfo to be used in your checks
- anything else: GlobalPolicy
You'll find policy sample at this location.
In SeAT 4, a permission is made of a scope and an ability. The ability is defined by the permissions configuration file and the scope is defined on registration.
This config file is then loaded from your app service provider as below:
For our API package, we have a database requirement. We need to store api tokens and the ip address that is allowed to use them. We are also going to store an access log (based on the config setting). We create migrations and models just like you would for a base Laravel 6 application. The only thing to remember is that your migrations for your package must be published (and specified in your service provider).
Registering these migrations looks like the following:
Releasing the plugin¶
The usual setup is to host the code on github and distribute the code via packagist.
When you submit your plugin on packagist, it will be installable like the other plugins by adding
<vendor>/<package> to the appropriate section of your